@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
@@ -137,7 +137,7 @@ class BasedService extends import_BaseService.BaseService {
137
137
  role: obj.role,
138
138
  joinedAt: Date.now()
139
139
  });
140
- await Promise.all([
140
+ return await Promise.all([
141
141
  this._client.call("db:set", {
142
142
  $id: userId,
143
143
  memberProjects: { $add: membershipId }
@@ -147,7 +147,6 @@ class BasedService extends import_BaseService.BaseService {
147
147
  members: { $add: membershipId }
148
148
  })
149
149
  ]);
150
- return await this._client.call("db:set", obj);
151
150
  } catch (error) {
152
151
  throw new Error(`Failed to set bucket: ${error.message}`);
153
152
  }
@@ -185,6 +184,7 @@ class BasedService extends import_BaseService.BaseService {
185
184
  throw new Error(`Query failed: ${error.message}`);
186
185
  }
187
186
  }
187
+ // DEPRECATED
188
188
  async fetchUser(userId) {
189
189
  if (!userId) {
190
190
  throw new Error("User Id is required");
@@ -192,15 +192,113 @@ class BasedService extends import_BaseService.BaseService {
192
192
  return await this.query("db", (0, import_basedQuerys.buildUserQuery)(userId));
193
193
  }
194
194
  async getUser(userId) {
195
- this._requireReady();
196
195
  if (!userId) {
197
- throw new Error("User Id is required");
198
- }
199
- try {
200
- return await this.call("users:get", { userId });
201
- } catch (error) {
202
- throw new Error(`Failed to get user: ${error.message}`);
203
- }
196
+ throw new Error("UserId is required");
197
+ }
198
+ const userData = await this._client.query("db", (0, import_basedQuerys.buildGetUserDataQuery)(userId)).get();
199
+ const user = {
200
+ id: userData.id,
201
+ name: userData.name,
202
+ email: userData.email,
203
+ username: userData.username,
204
+ globalRole: userData.globalRole,
205
+ createdAt: userData.createdAt,
206
+ updatedAt: userData.updatedAt
207
+ };
208
+ if (!user) {
209
+ throw new Error("User not found");
210
+ }
211
+ let memberProjects = [];
212
+ if (userData.memberProjects && userData.memberProjects.length > 0) {
213
+ const projectKeys = userData.memberProjects.filter((membership) => membership.project && membership.project.key).map((membership) => membership.project.key);
214
+ if (projectKeys.length > 0) {
215
+ const allProjects = await this._fetchProjectsByKeysInChunks(projectKeys);
216
+ memberProjects = userData.memberProjects.filter((membership) => membership.project && membership.project.key).map((membership) => {
217
+ const projectKey = membership.project.key;
218
+ const correctProject = allProjects.find(
219
+ (p) => p.key === projectKey
220
+ );
221
+ return {
222
+ project: correctProject || membership.project,
223
+ role: membership.role,
224
+ updatedAt: membership.updatedAt,
225
+ createdAt: membership.createdAt
226
+ };
227
+ });
228
+ }
229
+ } else {
230
+ console.log(`[getUser] No member projects found with ID: ${userId}`);
231
+ }
232
+ const formattedProjects = memberProjects.filter((membership) => membership.project).map((membership) => ({
233
+ id: membership.project.id,
234
+ name: membership.project.name,
235
+ key: membership.project.key,
236
+ thumbnail: membership.project.thumbnail,
237
+ icon: membership.project.icon,
238
+ tier: membership.project.tier,
239
+ visibility: membership.project.visibility,
240
+ access: membership.project.access,
241
+ role: membership.role,
242
+ joinedAt: membership.createdAt,
243
+ updatedAt: membership.updatedAt,
244
+ members: membership.project.members
245
+ })) || [];
246
+ return {
247
+ id: userId,
248
+ name: user.name,
249
+ email: user.email,
250
+ username: user.username,
251
+ globalRole: user.globalRole,
252
+ projects: formattedProjects,
253
+ createdAt: user.createdAt,
254
+ updatedAt: user.updatedAt
255
+ };
256
+ }
257
+ /**
258
+ * Fetches projects by keys in chunks with adaptive chunk sizing
259
+ * @param {Array<string>} allKeys - All project keys to fetch
260
+ * @returns {Promise<Array>} - All projects found
261
+ */
262
+ async _fetchProjectsByKeysInChunks(allKeys) {
263
+ if (!allKeys.length) {
264
+ return [];
265
+ }
266
+ const INITIAL_CHUNK_SIZE = 50;
267
+ const MAX_CHUNK_SIZE = 1e3;
268
+ const MIN_CHUNK_SIZE = 10;
269
+ const optimalChunkSize = INITIAL_CHUNK_SIZE;
270
+ const processChunks = async (keys, size) => {
271
+ if (!keys.length) {
272
+ return [];
273
+ }
274
+ const chunks = [];
275
+ for (let i = 0; i < keys.length; i += size) {
276
+ chunks.push(keys.slice(i, i + size));
277
+ }
278
+ try {
279
+ const result = await this._client.query("db", (0, import_basedQuerys.buildGetProjectsByKeysQuery)(chunks[0])).get();
280
+ const newSize = Math.min(Math.floor(size * 1.5), MAX_CHUNK_SIZE);
281
+ const firstChunkProjects = result.projects || [];
282
+ const remainingKeys = keys.slice(chunks[0].length);
283
+ const remainingProjects = await processChunks(remainingKeys, newSize);
284
+ return [...firstChunkProjects, ...remainingProjects];
285
+ } catch (error) {
286
+ if (error.message && error.message.includes("PayloadTooLarge")) {
287
+ const newSize = Math.max(Math.floor(size / 2), MIN_CHUNK_SIZE);
288
+ console.warn(`Reducing chunk size to ${newSize} due to PayloadTooLarge error`);
289
+ if (newSize === MIN_CHUNK_SIZE && chunks[0].length <= MIN_CHUNK_SIZE) {
290
+ console.error(`Cannot process chunk, skipping ${chunks[0].length} keys`);
291
+ const remainingKeys2 = keys.slice(chunks[0].length);
292
+ return processChunks(remainingKeys2, newSize);
293
+ }
294
+ return processChunks(keys, newSize);
295
+ }
296
+ console.error(`Error fetching projects: ${error.message}`);
297
+ const remainingKeys = keys.slice(chunks[0].length);
298
+ return processChunks(remainingKeys, size);
299
+ }
300
+ };
301
+ return await processChunks(allKeys, optimalChunkSize);
204
302
  }
205
303
  async getUserByEmail(email) {
206
304
  this._requireReady();
@@ -362,31 +460,26 @@ class BasedService extends import_BaseService.BaseService {
362
460
  throw new Error(`Failed to create project: ${error.message}`);
363
461
  }
364
462
  }
365
- async getProject(projectId, fields = { $all: true }) {
463
+ async getProject(projectId) {
366
464
  this._requireReady();
367
465
  if (!projectId) {
368
466
  throw new Error("Project ID is required");
369
467
  }
370
468
  try {
371
- return await this.call("projects:get", {
372
- projectId,
373
- fields: { ...fields }
374
- });
469
+ return await this._client.query("db", (0, import_basedQuerys.buildGetProjectDataQuery)(projectId)).get();
375
470
  } catch (error) {
376
471
  throw new Error(`Failed to get project: ${error.message}`);
377
472
  }
378
473
  }
379
- async getProjectByKey(key, fields = { $all: true }) {
474
+ async getProjectByKey(key) {
380
475
  this._requireReady();
381
476
  try {
382
- return await this.call("projects:get", {
383
- key,
384
- fields: { ...fields }
385
- });
477
+ return await this._client.query("db", (0, import_basedQuerys.buildGetProjectByKeyDataQuery)(key)).get();
386
478
  } catch (error) {
387
479
  throw new Error(`Failed to get project by key: ${error.message}`);
388
480
  }
389
481
  }
482
+ // DEPRECATED
390
483
  async fetchProject(projectId) {
391
484
  this._requireReady();
392
485
  if (!projectId) {
@@ -467,7 +560,9 @@ class BasedService extends import_BaseService.BaseService {
467
560
  pkg = 2,
468
561
  seats = 1,
469
562
  interval = "monthly",
470
- plan = "startup"
563
+ plan = "startup",
564
+ successUrl = `${window.location.origin}/success`,
565
+ cancelUrl = `${window.location.origin}/pricing`
471
566
  }) {
472
567
  this._requireReady();
473
568
  const prices = {
@@ -489,7 +584,9 @@ class BasedService extends import_BaseService.BaseService {
489
584
  return await this.call("checkout", {
490
585
  price,
491
586
  seats,
492
- projectId
587
+ projectId,
588
+ successUrl,
589
+ cancelUrl
493
590
  });
494
591
  } catch (error) {
495
592
  throw new Error(`Failed to checkout: ${error.message}`);
@@ -664,16 +761,425 @@ class BasedService extends import_BaseService.BaseService {
664
761
  * @param {string} newKey - The new key of the project (optional)
665
762
  * @returns {Promise<Object>} The duplicated project
666
763
  */
667
- async duplicateProject(projectId, newName, newKey) {
764
+ async duplicateProject(projectId, newName, newKey, targetUserId) {
668
765
  this._requireReady();
669
766
  try {
670
767
  return await this.call("projects:duplicate", {
671
768
  projectId,
672
769
  newName,
673
- newKey
770
+ newKey,
771
+ targetUserId
674
772
  });
675
773
  } catch (error) {
676
774
  throw new Error(`Failed to duplicate project: ${error.message}`);
677
775
  }
678
776
  }
777
+ /**
778
+ * List available subscription plans
779
+ * @param {Object} options - Options for filtering plans
780
+ * @param {number} options.page - Page number (default: 1)
781
+ * @param {number} options.limit - Number of plans per page (default: 20)
782
+ * @param {string} options.status - Filter plans by status (admin only)
783
+ * @param {boolean} options.includeSubscriptionCounts - Include subscription counts (admin only)
784
+ * @param {string} options.sortBy - Field to sort by (default: 'displayOrder')
785
+ * @param {string} options.sortOrder - Sort order ('asc' or 'desc', default: 'asc')
786
+ * @returns {Promise<Object>} List of plans with pagination info
787
+ */
788
+ async listPlans(options = {}) {
789
+ this._requireReady();
790
+ try {
791
+ return await this.call("plans:list", options);
792
+ } catch (error) {
793
+ throw new Error(`Failed to list plans: ${error.message}`);
794
+ }
795
+ }
796
+ /**
797
+ * Subscribe to a plan
798
+ * @param {string} planId - ID of the plan to subscribe to
799
+ * @param {Object} options - Options for the subscription
800
+ * @param {string} options.paymentMethod - Payment method
801
+ * @param {Object} options.billingAddress - Billing address
802
+ * @param {string} options.stripeCustomerId - Stripe customer ID
803
+ * @param {string} options.stripeSubscriptionId - Stripe subscription ID
804
+ * @returns {Promise<Object>} Subscription details
805
+ */
806
+ async subscribeToPlan(planId, options = {}) {
807
+ this._requireReady();
808
+ try {
809
+ return await this.call("subscriptions:create", { planId, ...options });
810
+ } catch (error) {
811
+ throw new Error(`Failed to subscribe to plan: ${error.message}`);
812
+ }
813
+ }
814
+ /**
815
+ * Get details of user's current subscription
816
+ * @param {string} subscriptionId - ID of the subscription to get details for
817
+ * @returns {Promise<Object>} Subscription details
818
+ */
819
+ async getSubscriptionDetails(subscriptionId) {
820
+ this._requireReady();
821
+ try {
822
+ return await this.call("subscriptions:details", {
823
+ subscriptionId
824
+ });
825
+ } catch (error) {
826
+ throw new Error(`Failed to get subscription details: ${error.message}`);
827
+ }
828
+ }
829
+ /**
830
+ * Check if the current subscription is active
831
+ * @param {string} subscriptionId - ID of the subscription to check
832
+ * @returns {Promise<Object>} Subscription status info
833
+ */
834
+ async checkSubscriptionStatus(subscriptionId) {
835
+ this._requireReady();
836
+ try {
837
+ return await this.call("subscriptions:check-status", {
838
+ subscriptionId
839
+ });
840
+ } catch (error) {
841
+ throw new Error(`Failed to check subscription status: ${error.message}`);
842
+ }
843
+ }
844
+ /**
845
+ * Upgrade the current subscription to a new plan
846
+ * @param {string} planId - ID of the plan to upgrade to
847
+ * @param {string} stripeSubscriptionId - ID of the Stripe subscription to upgrade
848
+ * @returns {Promise<Object>} Updated subscription details
849
+ */
850
+ async upgradeSubscription(planId, stripeSubscriptionId) {
851
+ this._requireReady();
852
+ try {
853
+ return await this.call("subscriptions:upgrade", {
854
+ planId,
855
+ stripeSubscriptionId
856
+ });
857
+ } catch (error) {
858
+ throw new Error(`Failed to upgrade subscription: ${error.message}`);
859
+ }
860
+ }
861
+ /**
862
+ * Downgrade the current subscription to a new plan
863
+ * @param {string} planId - ID of the plan to downgrade to
864
+ * @param {boolean} applyImmediately - Whether to apply the downgrade immediately
865
+ * @param {string} stripeSubscriptionId - ID of the Stripe subscription to downgrade
866
+ * @returns {Promise<Object>} Updated subscription details
867
+ */
868
+ async downgradeSubscription(planId, stripeSubscriptionId, applyImmediately = false) {
869
+ this._requireReady();
870
+ try {
871
+ return await this.call("subscriptions:downgrade", {
872
+ planId,
873
+ applyImmediately,
874
+ stripeSubscriptionId
875
+ });
876
+ } catch (error) {
877
+ throw new Error(`Failed to downgrade subscription: ${error.message}`);
878
+ }
879
+ }
880
+ /**
881
+ * Cancel the current subscription
882
+ * @param {boolean} cancelImmediately - Whether to cancel immediately or at period end
883
+ * @param {string} reason - Reason for cancellation
884
+ * @returns {Promise<Object>} Result of cancellation
885
+ */
886
+ async cancelSubscription(cancelImmediately = false, reason = "") {
887
+ this._requireReady();
888
+ try {
889
+ return await this.call("subscriptions:cancel", {
890
+ cancelImmediately,
891
+ reason
892
+ });
893
+ } catch (error) {
894
+ throw new Error(`Failed to cancel subscription: ${error.message}`);
895
+ }
896
+ }
897
+ /**
898
+ * Reactivate a subscription that was scheduled for cancellation
899
+ * @returns {Promise<Object>} Updated subscription details
900
+ */
901
+ async reactivateSubscription(stripeSubscriptionId) {
902
+ this._requireReady();
903
+ try {
904
+ return await this.call("subscriptions:reactivate", {
905
+ stripeSubscriptionId
906
+ });
907
+ } catch (error) {
908
+ throw new Error(`Failed to reactivate subscription: ${error.message}`);
909
+ }
910
+ }
911
+ /**
912
+ * Generate an invoice for the current subscription
913
+ * @param {string} subscriptionId - ID of the subscription to generate the invoice for
914
+ * @param {boolean} forceGenerate - Whether to force the generation of the invoice
915
+ * @param {Array} customItems - Custom items to add to the invoice
916
+ * @returns {Promise<Object>} Generated invoice
917
+ */
918
+ async generateInvoice(subscriptionId, forceGenerate = false, customItems = []) {
919
+ this._requireReady();
920
+ try {
921
+ return await this.call("billing:generate-invoice", {
922
+ subscriptionId,
923
+ forceGenerate,
924
+ customItems
925
+ });
926
+ } catch (error) {
927
+ throw new Error(`Failed to generate invoice: ${error.message}`);
928
+ }
929
+ }
930
+ /**
931
+ * Get usage report for the current subscription
932
+ * @param {string} subscriptionId - ID of the subscription to get the usage report for
933
+ * @param {boolean} forceRefresh - Whether to force the refresh of the usage report
934
+ * @returns {Promise<Object>} Usage report
935
+ */
936
+ async getUsageReport(subscriptionId, forceRefresh = false) {
937
+ this._requireReady();
938
+ try {
939
+ return await this.call("subscriptions:get-usage-report", {
940
+ subscriptionId,
941
+ forceRefresh
942
+ });
943
+ } catch (error) {
944
+ throw new Error(`Failed to get usage report: ${error.message}`);
945
+ }
946
+ }
947
+ /**
948
+ * Invite a user to be an account owner for the current subscription
949
+ * @param {string} email - Email of the user to invite
950
+ * @param {string} name - Name of the user to invite
951
+ * @param {string} callbackUrl - URL to redirect the user to after accepting the invitation
952
+ * @returns {Promise<Object>} Result of invitation
953
+ */
954
+ async inviteAccountOwner(email, name, callbackUrl) {
955
+ this._requireReady();
956
+ try {
957
+ return await this.call("subscriptions:invite", {
958
+ email,
959
+ name,
960
+ callbackUrl
961
+ });
962
+ } catch (error) {
963
+ throw new Error(`Failed to invite account owner: ${error.message}`);
964
+ }
965
+ }
966
+ /**
967
+ * Accept an invitation to become an account owner
968
+ * @param {string} token - Invitation token
969
+ * @returns {Promise<Object>} Result of accepting invitation
970
+ */
971
+ async acceptOwnerInvitation(token) {
972
+ this._requireReady();
973
+ try {
974
+ return await this.call("subscriptions:accept-owner-invitation", {
975
+ token
976
+ });
977
+ } catch (error) {
978
+ throw new Error(`Failed to accept owner invitation: ${error.message}`);
979
+ }
980
+ }
981
+ /**
982
+ * Remove an account owner from the current subscription
983
+ * @param {string} userId - ID of the user to remove
984
+ * @returns {Promise<Object>} Result of removal
985
+ */
986
+ async removeAccountOwner(userId) {
987
+ this._requireReady();
988
+ try {
989
+ return await this.call("subscriptions:remove-account-owner", {
990
+ targetUserId: userId
991
+ });
992
+ } catch (error) {
993
+ throw new Error(`Failed to remove account owner: ${error.message}`);
994
+ }
995
+ }
996
+ /**
997
+ * Check if a resource limit has been reached
998
+ * @param {string} resourceType - Type of resource to check
999
+ * @param {string} projectId - ID of the project to check the resource limit for
1000
+ * @param {number} quantity - Amount being requested
1001
+ * @param {string} userId - ID of user to check (admin only, defaults to current user)
1002
+ * @returns {Promise<Object>} Result of check
1003
+ */
1004
+ async checkResourceLimit(resourceType, projectId, quantity = 1, userId = "") {
1005
+ this._requireReady();
1006
+ try {
1007
+ return await this.call("subscriptions:check-resource-limit", {
1008
+ resourceType,
1009
+ projectId,
1010
+ quantity,
1011
+ userId
1012
+ });
1013
+ } catch (error) {
1014
+ throw new Error(`Failed to check resource limit: ${error.message}`);
1015
+ }
1016
+ }
1017
+ /**
1018
+ * Check if a user has access to a feature
1019
+ * @param {string} featureKey - Key of the feature to check
1020
+ * @param {string} userId - ID of user to check (admin only, defaults to current user)
1021
+ * @returns {Promise<Object>} Access check result
1022
+ */
1023
+ async checkFeatureAccess(featureKey, userId) {
1024
+ this._requireReady();
1025
+ try {
1026
+ return await this.call("features:check-access", {
1027
+ featureKey,
1028
+ userId
1029
+ });
1030
+ } catch (error) {
1031
+ throw new Error(`Failed to check feature access: ${error.message}`);
1032
+ }
1033
+ }
1034
+ /**
1035
+ * Get all features available to the current user
1036
+ * @returns {Promise<Object>} Available features
1037
+ */
1038
+ async getUserFeatures() {
1039
+ this._requireReady();
1040
+ try {
1041
+ return await this.call("subscriptions:get-user-features", {});
1042
+ } catch (error) {
1043
+ throw new Error(`Failed to get user features: ${error.message}`);
1044
+ }
1045
+ }
1046
+ /**
1047
+ * Get all available features across all plans
1048
+ * @param {string} subscriptionId - ID of the subscription to get the available features for
1049
+ * @param {string} userId - ID of user to get the available features for (admin only, defaults to current user)
1050
+ * @param {boolean} includeDetails - Whether to include details about the features
1051
+ * @returns {Promise<Object>} Available features
1052
+ */
1053
+ async getAvailableFeatures(subscriptionId, userId = "", includeDetails = false) {
1054
+ this._requireReady();
1055
+ try {
1056
+ return await this.call("features:get-available", {
1057
+ subscriptionId,
1058
+ userId,
1059
+ includeDetails
1060
+ });
1061
+ } catch (error) {
1062
+ throw new Error(`Failed to get available features: ${error.message}`);
1063
+ }
1064
+ }
1065
+ /**
1066
+ * List all feature flags (admin only)
1067
+ * @param {Object} options - Options for listing feature flags
1068
+ * @param {number} options.page - Page number
1069
+ * @param {number} options.limit - Number of items per page
1070
+ * @param {string} options.status - Filter by status
1071
+ * @returns {Promise<Object>} List of feature flags
1072
+ */
1073
+ async listFeatureFlags(options = {}) {
1074
+ this._requireReady();
1075
+ try {
1076
+ return await this.call("features:list", options);
1077
+ } catch (error) {
1078
+ throw new Error(`Failed to list feature flags: ${error.message}`);
1079
+ }
1080
+ }
1081
+ /**
1082
+ * Update a feature flag (admin only)
1083
+ * @param {string} flagId - ID of the feature flag to update
1084
+ * @param {Object} options - Update data
1085
+ * @param {string} options.key - Key of the feature flag
1086
+ * @param {string} options.name - Name of the feature flag
1087
+ * @param {string} options.description - Description of the feature flag
1088
+ * @param {string} options.defaultValue - Default value of the feature flag
1089
+ * @param {Object} options.planOverrides - Plan overrides for the feature flag
1090
+ * @param {Object} options.userOverrides - User overrides for the feature flag
1091
+ * @param {string} options.status - Status of the feature flag
1092
+ * @returns {Promise<Object>} Updated feature flag
1093
+ */
1094
+ async updateFeatureFlag(flagId, options) {
1095
+ this._requireReady();
1096
+ try {
1097
+ return await this.call("features:update", {
1098
+ flagId,
1099
+ ...options
1100
+ });
1101
+ } catch (error) {
1102
+ throw new Error(`Failed to update feature flag: ${error.message}`);
1103
+ }
1104
+ }
1105
+ /**
1106
+ * Remove a feature flag (admin only)
1107
+ * @param {string} flagId - ID of the feature flag to remove
1108
+ * @returns {Promise<Object>} Result of removal
1109
+ */
1110
+ async removeFeatureFlag(flagId) {
1111
+ this._requireReady();
1112
+ try {
1113
+ return await this.call("features:remove", {
1114
+ flagId
1115
+ });
1116
+ } catch (error) {
1117
+ throw new Error(`Failed to remove feature flag: ${error.message}`);
1118
+ }
1119
+ }
1120
+ /**
1121
+ * Batch update feature flags (admin only)
1122
+ * @param {Array} operations - Array of feature flag operations
1123
+ * @param {string} operations.flagId - ID of the feature flag to update
1124
+ * @param {string} operations.key - Key of the feature flag
1125
+ * @param {string} operations.name - Name of the feature flag
1126
+ * @param {string} operations.description - Description of the feature flag
1127
+ * @param {boolean} operations.defaultValue - Default value of the feature flag
1128
+ * @param {Object} operations.planOverrides - Plan overrides for the feature flag
1129
+ * @param {Object} operations.userOverrides - User overrides for the feature flag
1130
+ * @param {string} operations.status - Status of the feature flag
1131
+ * @returns {Promise<Object>} Result of batch update
1132
+ */
1133
+ async batchUpdateFeatureFlags(operations) {
1134
+ this._requireReady();
1135
+ try {
1136
+ return await this.call("features:batch-update", {
1137
+ operations
1138
+ });
1139
+ } catch (error) {
1140
+ throw new Error(`Failed to batch update feature flags: ${error.message}`);
1141
+ }
1142
+ }
1143
+ /**
1144
+ * Update plan details (admin only)
1145
+ * @param {string} planId - ID of the plan to update
1146
+ * @param {Object} data - Update data
1147
+ * @param {string} data.name - Display name (e.g., "Pro", "Team")
1148
+ * @param {string} data.description - Marketing description
1149
+ * @param {number} data.price - Monthly price in cents
1150
+ * @param {number} data.annualPrice - Annual price in cents (discounted)
1151
+ * @param {number} data.lifetimePrice - One-time lifetime price in cents
1152
+ * @param {string} data.billingType - Payment type
1153
+ * @param {string} data.status - Status
1154
+ * @param {Object} data.features - Detailed feature configuration
1155
+ * @returns {Promise<Object>} Updated plan
1156
+ */
1157
+ async updatePlanDetails(planId, data) {
1158
+ this._requireReady();
1159
+ try {
1160
+ return await this.call("plans:update-details", {
1161
+ planId,
1162
+ data
1163
+ });
1164
+ } catch (error) {
1165
+ throw new Error(`Failed to update plan details: ${error.message}`);
1166
+ }
1167
+ }
1168
+ /**
1169
+ * Update plan status (admin only)
1170
+ * @param {string} planId - ID of the plan to update
1171
+ * @param {string} status - New status
1172
+ * @returns {Promise<Object>} Updated plan
1173
+ */
1174
+ async updatePlanStatus(planId, status) {
1175
+ this._requireReady();
1176
+ try {
1177
+ return await this.call("plans:update-status", {
1178
+ planId,
1179
+ status
1180
+ });
1181
+ } catch (error) {
1182
+ throw new Error(`Failed to update plan status: ${error.message}`);
1183
+ }
1184
+ }
679
1185
  }