@explorins/pers-sdk-react-native 2.1.14-beta.0 → 2.1.17

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 (61) hide show
  1. package/dist/hooks/index.d.ts +3 -2
  2. package/dist/hooks/index.d.ts.map +1 -1
  3. package/dist/hooks/useAnalytics.d.ts +1 -1
  4. package/dist/hooks/useAnalytics.d.ts.map +1 -1
  5. package/dist/hooks/useAuth.d.ts +3 -3
  6. package/dist/hooks/useAuth.d.ts.map +1 -1
  7. package/dist/hooks/useAuth.js +5 -17
  8. package/dist/hooks/useBusiness.d.ts +3 -2
  9. package/dist/hooks/useBusiness.d.ts.map +1 -1
  10. package/dist/hooks/useBusiness.js +28 -2
  11. package/dist/hooks/useCampaigns.d.ts +10 -4
  12. package/dist/hooks/useCampaigns.d.ts.map +1 -1
  13. package/dist/hooks/useCampaigns.js +36 -8
  14. package/dist/hooks/useDonations.d.ts +1 -1
  15. package/dist/hooks/useDonations.d.ts.map +1 -1
  16. package/dist/hooks/usePurchases.d.ts +2 -2
  17. package/dist/hooks/usePurchases.d.ts.map +1 -1
  18. package/dist/hooks/useRedemptions.d.ts +10 -2
  19. package/dist/hooks/useRedemptions.d.ts.map +1 -1
  20. package/dist/hooks/useRedemptions.js +32 -4
  21. package/dist/hooks/useTenants.d.ts +1 -1
  22. package/dist/hooks/useTenants.d.ts.map +1 -1
  23. package/dist/hooks/useTokenBalances.d.ts +1 -1
  24. package/dist/hooks/useTokenBalances.d.ts.map +1 -1
  25. package/dist/hooks/useTokenBalances.js +1 -1
  26. package/dist/hooks/useTokens.d.ts +1 -1
  27. package/dist/hooks/useTokens.d.ts.map +1 -1
  28. package/dist/hooks/useTransactions.d.ts +1 -1
  29. package/dist/hooks/useTransactions.d.ts.map +1 -1
  30. package/dist/hooks/useTriggerSources.d.ts +1 -1
  31. package/dist/hooks/useTriggerSources.d.ts.map +1 -1
  32. package/dist/hooks/useUserStatus.d.ts +1 -1
  33. package/dist/hooks/useUserStatus.d.ts.map +1 -1
  34. package/dist/hooks/useUsers.d.ts +1 -1
  35. package/dist/hooks/useUsers.d.ts.map +1 -1
  36. package/dist/index-CzLxqePZ.js +7717 -0
  37. package/dist/index-CzLxqePZ.js.map +1 -0
  38. package/dist/index.d.ts +4 -3
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1693 -254
  41. package/dist/index.js.map +1 -1
  42. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  43. package/dist/providers/PersSDKProvider.js +7 -17
  44. package/package.json +3 -3
  45. package/src/hooks/index.ts +5 -2
  46. package/src/hooks/useAnalytics.ts +1 -1
  47. package/src/hooks/useAuth.ts +6 -20
  48. package/src/hooks/useBusiness.ts +32 -3
  49. package/src/hooks/useCampaigns.ts +47 -12
  50. package/src/hooks/useDonations.ts +1 -1
  51. package/src/hooks/usePurchases.ts +1 -1
  52. package/src/hooks/useRedemptions.ts +43 -6
  53. package/src/hooks/useTenants.ts +1 -1
  54. package/src/hooks/useTokenBalances.ts +1 -1
  55. package/src/hooks/useTokens.ts +1 -1
  56. package/src/hooks/useTransactions.ts +1 -1
  57. package/src/hooks/useTriggerSources.ts +1 -1
  58. package/src/hooks/useUserStatus.ts +1 -1
  59. package/src/hooks/useUsers.ts +1 -1
  60. package/src/index.ts +6 -3
  61. package/src/providers/PersSDKProvider.tsx +8 -17
package/dist/index.js CHANGED
@@ -3563,7 +3563,8 @@ function isValidTransactionRelation(value) {
3563
3563
  */
3564
3564
  const VALID_CAMPAIGN_RELATIONS = [
3565
3565
  'triggerSources',
3566
- 'businesses'
3566
+ 'businesses',
3567
+ 'claimCount'
3567
3568
  ];
3568
3569
  /**
3569
3570
  * Type guard to validate campaign include relation at runtime
@@ -3596,6 +3597,23 @@ function isValidCampaignClaimRelation(value) {
3596
3597
  return VALID_CAMPAIGN_CLAIM_RELATIONS.includes(value);
3597
3598
  }
3598
3599
 
3600
+ /**
3601
+ * Array of all valid redemption include relations
3602
+ * Used for runtime validation
3603
+ */
3604
+ const VALID_REDEMPTION_RELATIONS = [
3605
+ 'redeemCount'
3606
+ ];
3607
+ /**
3608
+ * Type guard to validate redemption include relation at runtime
3609
+ *
3610
+ * @param value - String to validate
3611
+ * @returns True if value is a valid RedemptionIncludeRelation
3612
+ */
3613
+ function isValidRedemptionRelation(value) {
3614
+ return VALID_REDEMPTION_RELATIONS.includes(value);
3615
+ }
3616
+
3599
3617
  /**
3600
3618
  * Array of all valid redemption redeem include relations
3601
3619
  * Used for runtime validation
@@ -3642,6 +3660,26 @@ function isValidUserRelation(value) {
3642
3660
  return VALID_USER_RELATIONS.includes(value);
3643
3661
  }
3644
3662
 
3663
+ /**
3664
+ * Normalizes filter options to use new field names while accepting deprecated ones.
3665
+ * Use at controller level to support both old and new API contracts.
3666
+ *
3667
+ * @example
3668
+ * // In controller
3669
+ * const filters = normalizeFilters({ isActive: true, sort: 'name' });
3670
+ * // Result: { active: true, sortBy: 'name', isActive: true, sort: 'name' }
3671
+ */
3672
+ function normalizeFilters(filters) {
3673
+ if (!filters)
3674
+ return undefined;
3675
+ return {
3676
+ ...filters,
3677
+ active: filters.active ?? filters.isActive,
3678
+ sortBy: filters.sortBy ?? filters.sort,
3679
+ sortOrder: filters.sortOrder ?? filters.order,
3680
+ };
3681
+ }
3682
+
3645
3683
  /**
3646
3684
  * Chain Data Types
3647
3685
  *
@@ -3711,6 +3749,17 @@ const isTokenExpired = (token, margin = 60) => {
3711
3749
  * Consolidated API and auth errors for fast SDK performance
3712
3750
  * Uses @explorins/pers-shared when available, fallback to SDK errors
3713
3751
  */
3752
+ /**
3753
+ * SDK-internal error codes for auth flow control
3754
+ * These are not backend errors - they're used for SDK-internal state management
3755
+ */
3756
+ const SdkErrorCodes = {
3757
+ TOKEN_REFRESH_NEEDED: 'TOKEN_REFRESH_NEEDED',
3758
+ PROVIDER_TOKEN_REFRESH_NEEDED: 'PROVIDER_TOKEN_REFRESH_NEEDED',
3759
+ LOGOUT_REQUIRED: 'LOGOUT_REQUIRED',
3760
+ NETWORK_ERROR: 'NETWORK_ERROR',
3761
+ API_ERROR: 'API_ERROR',
3762
+ };
3714
3763
  // Fast type guards and utilities
3715
3764
  class ErrorUtils {
3716
3765
  /**
@@ -3870,9 +3919,9 @@ class TokenRefreshNeeded extends Error {
3870
3919
  constructor(refreshToken) {
3871
3920
  super('Token refresh needed');
3872
3921
  this.refreshToken = refreshToken;
3873
- this.errorCode = 'TOKEN_REFRESH_NEEDED';
3874
- this.domain = 'auth';
3875
- this.category = 'SECURITY';
3922
+ this.errorCode = SdkErrorCodes.TOKEN_REFRESH_NEEDED;
3923
+ this.domain = Domains.AUTHENTICATION;
3924
+ this.category = exports.ErrorCategory.SECURITY;
3876
3925
  this.retryable = true;
3877
3926
  this.name = 'TokenRefreshNeeded';
3878
3927
  }
@@ -3881,9 +3930,9 @@ class ProviderTokenRefreshNeeded extends Error {
3881
3930
  constructor(providerToken) {
3882
3931
  super('Provider token refresh needed');
3883
3932
  this.providerToken = providerToken;
3884
- this.errorCode = 'PROVIDER_TOKEN_REFRESH_NEEDED';
3885
- this.domain = 'auth';
3886
- this.category = 'SECURITY';
3933
+ this.errorCode = SdkErrorCodes.PROVIDER_TOKEN_REFRESH_NEEDED;
3934
+ this.domain = Domains.AUTHENTICATION;
3935
+ this.category = exports.ErrorCategory.SECURITY;
3887
3936
  this.retryable = true;
3888
3937
  this.name = 'ProviderTokenRefreshNeeded';
3889
3938
  }
@@ -3891,9 +3940,9 @@ class ProviderTokenRefreshNeeded extends Error {
3891
3940
  class LogoutRequired extends Error {
3892
3941
  constructor(message) {
3893
3942
  super(message);
3894
- this.errorCode = 'LOGOUT_REQUIRED';
3895
- this.domain = 'auth';
3896
- this.category = 'SECURITY';
3943
+ this.errorCode = SdkErrorCodes.LOGOUT_REQUIRED;
3944
+ this.domain = Domains.AUTHENTICATION;
3945
+ this.category = exports.ErrorCategory.SECURITY;
3897
3946
  this.retryable = false;
3898
3947
  this.name = 'LogoutRequired';
3899
3948
  }
@@ -3909,25 +3958,40 @@ class PersApiError extends Error {
3909
3958
  this.name = 'PersApiError';
3910
3959
  // Use backend error details directly
3911
3960
  this.code = backendError?.code || 'API_ERROR';
3912
- this.domain = backendError?.domain || 'api';
3961
+ this.domain = backendError?.domain || ErrorDomains.EXTERNAL;
3913
3962
  this.category = backendError?.category || exports.ErrorCategory.TECHNICAL;
3914
3963
  this.retryable = backendError?.retryable ?? (statusCode ? statusCode >= 500 : false);
3915
3964
  this.message = backendError?.message || message;
3916
3965
  this.status = backendError?.status || statusCode || 500;
3917
3966
  this.title = backendError?.title || 'Request failed';
3918
3967
  this.detail = backendError?.detail || backendError?.message || message;
3968
+ this.details = backendError?.details;
3919
3969
  }
3920
3970
  }
3921
3971
  class AuthenticationError extends PersApiError {
3922
3972
  constructor(message, endpoint, method, backendError) {
3923
3973
  super(message, endpoint, method, 401, {
3924
3974
  ...backendError,
3925
- domain: 'authentication',
3975
+ domain: Domains.AUTHENTICATION,
3926
3976
  category: exports.ErrorCategory.SECURITY
3927
3977
  });
3928
3978
  this.name = 'AuthenticationError';
3929
3979
  }
3930
3980
  }
3981
+ class NetworkError extends Error {
3982
+ constructor(message) {
3983
+ super(message);
3984
+ this.code = SdkErrorCodes.NETWORK_ERROR;
3985
+ this.domain = ErrorDomains.EXTERNAL;
3986
+ this.category = exports.ErrorCategory.INFRASTRUCTURE;
3987
+ this.retryable = true;
3988
+ this.status = 0;
3989
+ this.title = 'Network error';
3990
+ this.name = 'NetworkError';
3991
+ this.message = message;
3992
+ this.detail = message;
3993
+ }
3994
+ }
3931
3995
  const ApiErrorDetector = {
3932
3996
  getErrorMessage: (error) => ErrorUtils.getMessage(error),
3933
3997
  getStatusCode: (error) => ErrorUtils.getStatus(error),
@@ -5221,6 +5285,10 @@ class BusinessApi {
5221
5285
  * limit: 25,
5222
5286
  * sanitize: 'soft'
5223
5287
  * });
5288
+ *
5289
+ * // Filter by tag(s)
5290
+ * const tagged = await businessApi.getBusinesses({ tag: 'restaurant' });
5291
+ * const multiTag = await businessApi.getBusinesses({ tag: ['restaurant', 'cafe'] });
5224
5292
  * ```
5225
5293
  */
5226
5294
  async getBusinesses(options) {
@@ -5228,6 +5296,15 @@ class BusinessApi {
5228
5296
  if (options?.active !== undefined) {
5229
5297
  params.set('active', String(options.active));
5230
5298
  }
5299
+ if (options?.search) {
5300
+ params.set('search', options.search);
5301
+ }
5302
+ // Support single tag or array of tags (multiple ?tags=a&tags=b)
5303
+ if (options?.tags) {
5304
+ const tagsArray = Array.isArray(options.tags) ? options.tags : [options.tags];
5305
+ // URLSearchParams supports multiple values with same key via append
5306
+ tagsArray.forEach(t => params.append('tags', t));
5307
+ }
5231
5308
  if (options?.sanitize) {
5232
5309
  params.set('sanitize', options.sanitize);
5233
5310
  }
@@ -5596,7 +5673,9 @@ class BusinessService {
5596
5673
  // ADMIN OPERATIONS
5597
5674
  // ==========================================
5598
5675
  /**
5599
- * ADMIN: Get all businesses with pagination (active and inactive)
5676
+ * ADMIN: Get all businesses with filtering and pagination
5677
+ * @param options - Filter and pagination options
5678
+ * @see BusinessFilterOptions for available filter options
5600
5679
  */
5601
5680
  async getBusinesses(options) {
5602
5681
  return this.businessApi.getBusinesses(options);
@@ -5915,7 +5994,9 @@ class CampaignApi {
5915
5994
  * Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all
5916
5995
  *
5917
5996
  * @param options - Filter, pagination, and include options
5918
- * @param options.include - Relations to include: 'triggerSources', 'businesses'
5997
+ * @param options.include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
5998
+ * @param options.startDate - Filter campaigns starting on or after this date
5999
+ * @param options.endDate - Filter campaigns ending on or before this date
5919
6000
  *
5920
6001
  * @example
5921
6002
  * ```typescript
@@ -5923,8 +6004,15 @@ class CampaignApi {
5923
6004
  * const campaigns = await api.getCampaigns({ include: ['triggerSources'] });
5924
6005
  * campaigns.data.forEach(c => console.log(c.included?.triggerSources));
5925
6006
  *
5926
- * // Get campaigns with both relations
5927
- * const full = await api.getCampaigns({ include: ['triggerSources', 'businesses'] });
6007
+ * // Get campaigns with claim count
6008
+ * const withCounts = await api.getCampaigns({ include: ['claimCount'] });
6009
+ * withCounts.data.forEach(c => console.log(c.claimCount));
6010
+ *
6011
+ * // Filter by date range
6012
+ * const dateFiltered = await api.getCampaigns({
6013
+ * startDate: new Date('2026-05-01'),
6014
+ * endDate: new Date('2026-12-31')
6015
+ * });
5928
6016
  * ```
5929
6017
  */
5930
6018
  async getCampaigns(options) {
@@ -5934,12 +6022,23 @@ class CampaignApi {
5934
6022
  params.push(`limit=${options?.limit || 50}`);
5935
6023
  if (options?.active !== undefined)
5936
6024
  params.push(`active=${options.active}`);
5937
- if (options?.tag)
5938
- params.push(`tag=${encodeURIComponent(options.tag)}`);
6025
+ // Support single tag or array of tags (multiple ?tags=a&tags=b)
6026
+ if (options?.tags) {
6027
+ const tagsArray = Array.isArray(options.tags) ? options.tags : [options.tags];
6028
+ tagsArray.forEach(t => params.push(`tags=${encodeURIComponent(t)}`));
6029
+ }
5939
6030
  if (options?.businessId)
5940
6031
  params.push(`businessId=${encodeURIComponent(options.businessId)}`);
5941
6032
  if (options?.search)
5942
6033
  params.push(`search=${encodeURIComponent(options.search)}`);
6034
+ if (options?.startDate) {
6035
+ const dateStr = options.startDate instanceof Date ? options.startDate.toISOString() : options.startDate;
6036
+ params.push(`startDate=${encodeURIComponent(dateStr)}`);
6037
+ }
6038
+ if (options?.endDate) {
6039
+ const dateStr = options.endDate instanceof Date ? options.endDate.toISOString() : options.endDate;
6040
+ params.push(`endDate=${encodeURIComponent(dateStr)}`);
6041
+ }
5943
6042
  if (options?.sortBy)
5944
6043
  params.push(`sortBy=${options.sortBy}`);
5945
6044
  if (options?.sortOrder)
@@ -5953,7 +6052,7 @@ class CampaignApi {
5953
6052
  * NEW: /campaigns/{id}
5954
6053
  *
5955
6054
  * @param id - Campaign UUID
5956
- * @param include - Relations to include: 'triggerSources', 'businesses'
6055
+ * @param include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
5957
6056
  *
5958
6057
  * @example
5959
6058
  * ```typescript
@@ -5961,6 +6060,10 @@ class CampaignApi {
5961
6060
  * const campaign = await api.getCampaignById('123', ['triggerSources', 'businesses']);
5962
6061
  * console.log(campaign.included?.triggerSources);
5963
6062
  * console.log(campaign.included?.businesses);
6063
+ *
6064
+ * // Get campaign with claim count
6065
+ * const withCount = await api.getCampaignById('123', ['claimCount']);
6066
+ * console.log(withCount.claimCount);
5964
6067
  * ```
5965
6068
  */
5966
6069
  async getCampaignById(id, include) {
@@ -6209,6 +6312,10 @@ class CampaignApi {
6209
6312
  params.set('userId', filters.userId);
6210
6313
  if (filters?.businessId)
6211
6314
  params.set('businessId', filters.businessId);
6315
+ if (filters?.dateFrom)
6316
+ params.set('dateFrom', filters.dateFrom instanceof Date ? filters.dateFrom.toISOString() : filters.dateFrom);
6317
+ if (filters?.dateTo)
6318
+ params.set('dateTo', filters.dateTo instanceof Date ? filters.dateTo.toISOString() : filters.dateTo);
6212
6319
  // Add include parameter
6213
6320
  if (include && include.length > 0) {
6214
6321
  include.forEach(relation => params.append('include', relation));
@@ -6306,15 +6413,7 @@ class CampaignService {
6306
6413
  /**
6307
6414
  * Get campaigns with pagination support
6308
6415
  * Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all
6309
- * @param options.active - Filter by active status (Admin only)
6310
- * @param options.tag - Filter by tag
6311
- * @param options.search - Free text search across name, description, and tags (case-insensitive)
6312
- * @param options.businessId - Filter by business engagement
6313
- * @param options.page - Page number (1-based, default: 1)
6314
- * @param options.limit - Items per page (default: 50)
6315
- * @param options.sortBy - Sort field
6316
- * @param options.sortOrder - Sort direction
6317
- * @param options.include - Relations to include: 'triggerSources', 'businesses'
6416
+ * @see CampaignFilterOptions for available filter options
6318
6417
  */
6319
6418
  async getCampaigns(options) {
6320
6419
  return this.campaignApi.getCampaigns(options);
@@ -6414,7 +6513,7 @@ class CampaignService {
6414
6513
  }
6415
6514
  /**
6416
6515
  * ADMIN: Get campaign claims with optional filters and pagination
6417
- * @param filters - Filter and pagination options
6516
+ * @param filters - Filter and pagination options including date range
6418
6517
  * @param include - Optional relations to include (campaign, user, business)
6419
6518
  */
6420
6519
  async getCampaignClaims(filters, include) {
@@ -6484,8 +6583,20 @@ class RedemptionApi {
6484
6583
  * - Public users: Get active redemptions only
6485
6584
  * - Admin users: Get all redemptions with optional filtering
6486
6585
  *
6487
- * @param options - Filter and pagination options (active, search, adminAccess, page, limit, sortBy, sortOrder)
6586
+ * @param options - Filter and pagination options (active, search, adminAccess, startDate, endDate, include, page, limit, sortBy, sortOrder)
6488
6587
  * @returns Paginated response with redemptions
6588
+ *
6589
+ * @example
6590
+ * ```typescript
6591
+ * // Get redemptions with redeem count included
6592
+ * const redemptions = await api.getRedemptions({ include: ['redeemCount'] });
6593
+ *
6594
+ * // Filter by date range
6595
+ * const dateFiltered = await api.getRedemptions({
6596
+ * startDate: new Date('2026-05-01'),
6597
+ * endDate: new Date('2026-06-30')
6598
+ * });
6599
+ * ```
6489
6600
  */
6490
6601
  async getRedemptions(options) {
6491
6602
  const params = [];
@@ -6496,10 +6607,25 @@ class RedemptionApi {
6496
6607
  params.push(`active=${options.active}`);
6497
6608
  if (options?.search)
6498
6609
  params.push(`search=${encodeURIComponent(options.search)}`);
6610
+ // Support single tag or array of tags (multiple ?tags=a&tags=b)
6611
+ if (options?.tags) {
6612
+ const tagsArray = Array.isArray(options.tags) ? options.tags : [options.tags];
6613
+ tagsArray.forEach(t => params.push(`tags=${encodeURIComponent(t)}`));
6614
+ }
6615
+ if (options?.startDate) {
6616
+ const dateStr = options.startDate instanceof Date ? options.startDate.toISOString() : options.startDate;
6617
+ params.push(`startDate=${encodeURIComponent(dateStr)}`);
6618
+ }
6619
+ if (options?.endDate) {
6620
+ const dateStr = options.endDate instanceof Date ? options.endDate.toISOString() : options.endDate;
6621
+ params.push(`endDate=${encodeURIComponent(dateStr)}`);
6622
+ }
6499
6623
  if (options?.sortBy)
6500
6624
  params.push(`sortBy=${options.sortBy}`);
6501
6625
  if (options?.sortOrder)
6502
6626
  params.push(`sortOrder=${options.sortOrder}`);
6627
+ if (options?.include?.length)
6628
+ params.push(`include=${options.include.join(',')}`);
6503
6629
  const response = await this.apiClient.get(`${this.basePath}?${params.join('&')}`);
6504
6630
  return normalizeToPaginated(response);
6505
6631
  }
@@ -6519,9 +6645,20 @@ class RedemptionApi {
6519
6645
  * PUBLIC: Get redemption by ID
6520
6646
  *
6521
6647
  * Updated: /redemption/:id → /redemptions/:id
6648
+ *
6649
+ * @param id - Redemption UUID
6650
+ * @param include - Relations to include: 'redeemCount'
6651
+ *
6652
+ * @example
6653
+ * ```typescript
6654
+ * // Get redemption with redeem count
6655
+ * const redemption = await api.getRedemptionById('123', ['redeemCount']);
6656
+ * console.log(redemption.redeemCount);
6657
+ * ```
6522
6658
  */
6523
- async getRedemptionById(id) {
6524
- return this.apiClient.get(`${this.basePath}/${id}`);
6659
+ async getRedemptionById(id, include) {
6660
+ const params = include?.length ? `?include=${include.join(',')}` : '';
6661
+ return this.apiClient.get(`${this.basePath}/${id}${params}`);
6525
6662
  }
6526
6663
  /**
6527
6664
  * PUBLIC: Get available supply for redemption
@@ -6558,7 +6695,7 @@ class RedemptionApi {
6558
6695
  * - Users: See only their own redeems (userId/businessId filters ignored)
6559
6696
  * - Admins: Can filter by userId, businessId, redemptionId, or status
6560
6697
  *
6561
- * @param filters - Filter and pagination options (redemptionId, userId, businessId, status, myRedeems, page, limit, sortBy, sortOrder)
6698
+ * @param filters - Filter and pagination options (redemptionId, userId, businessId, status, dateFrom, dateTo, myRedeems, page, limit, sortBy, sortOrder)
6562
6699
  * @param include - Optional relations to include (redemption, user, business, transactions)
6563
6700
  * @returns Paginated response with redemption redeems
6564
6701
  */
@@ -6574,6 +6711,12 @@ class RedemptionApi {
6574
6711
  if (filters?.status) {
6575
6712
  params.append('status', filters.status);
6576
6713
  }
6714
+ if (filters?.dateFrom) {
6715
+ params.append('dateFrom', filters.dateFrom instanceof Date ? filters.dateFrom.toISOString() : filters.dateFrom);
6716
+ }
6717
+ if (filters?.dateTo) {
6718
+ params.append('dateTo', filters.dateTo instanceof Date ? filters.dateTo.toISOString() : filters.dateTo);
6719
+ }
6577
6720
  // Add include parameter
6578
6721
  if (include && include.length > 0) {
6579
6722
  include.forEach(relation => params.append('include', relation));
@@ -6590,6 +6733,10 @@ class RedemptionApi {
6590
6733
  params.append('businessId', filters.businessId);
6591
6734
  if (filters?.status)
6592
6735
  params.append('status', filters.status);
6736
+ if (filters?.dateFrom)
6737
+ params.append('dateFrom', filters.dateFrom instanceof Date ? filters.dateFrom.toISOString() : filters.dateFrom);
6738
+ if (filters?.dateTo)
6739
+ params.append('dateTo', filters.dateTo instanceof Date ? filters.dateTo.toISOString() : filters.dateTo);
6593
6740
  // Add include parameter
6594
6741
  if (include && include.length > 0) {
6595
6742
  include.forEach(relation => params.append('include', relation));
@@ -6730,10 +6877,14 @@ class RedemptionService {
6730
6877
  * UNIFIED: Get redemptions with intelligent access control and pagination
6731
6878
  * @param options.active - Filter by active status
6732
6879
  * @param options.search - Free text search across name, description, tags (case-insensitive)
6880
+ * @param options.tags - Filter by tag(s) - single tag or array for OR match
6733
6881
  * @param options.adminAccess - Force admin access
6882
+ * @param options.startDate - Filter redemptions starting on or after this date
6883
+ * @param options.endDate - Filter redemptions ending on or before this date
6884
+ * @param options.include - Include relations: 'redeemCount'
6734
6885
  * @param options.page - Page number (1-indexed)
6735
6886
  * @param options.limit - Results per page
6736
- * @param options.sortBy - Field to sort by ('name' | 'createdAt')
6887
+ * @param options.sortBy - Field to sort by ('name' | 'createdAt' | 'startDate')
6737
6888
  * @param options.sortOrder - Sort direction ('ASC' | 'DESC')
6738
6889
  */
6739
6890
  async getRedemptions(options) {
@@ -6768,7 +6919,7 @@ class RedemptionService {
6768
6919
  }
6769
6920
  /**
6770
6921
  * UNIFIED: Get redemption redeems with filtering and pagination
6771
- * @param filters - Filter options (redemptionId, userId, businessId, status, myRedeems)
6922
+ * @param filters - Filter options (redemptionId, userId, businessId, status, dateFrom, dateTo, myRedeems)
6772
6923
  * @param include - Optional relations to include (redemption, user, business, transactions)
6773
6924
  */
6774
6925
  async getRedemptionRedeems(filters, include) {
@@ -6788,7 +6939,7 @@ class RedemptionService {
6788
6939
  // ==========================================
6789
6940
  /**
6790
6941
  * Convenience: Get redemptions as admin with filtering and search
6791
- * @param options - Filter options (active, search, pagination)
6942
+ * @param options - Filter options (active, search, tags, pagination)
6792
6943
  */
6793
6944
  async getRedemptionsAsAdmin(options) {
6794
6945
  return this.redemptionApi.getRedemptions({ ...options, adminAccess: true });
@@ -7239,6 +7390,82 @@ class TransactionService {
7239
7390
  return this.transactionApi.getTransactionAnalytics(analyticsRequest);
7240
7391
  }
7241
7392
  }
7393
+
7394
+ // ============================================================================
7395
+ // TRANSACTION RESPONSE HELPERS
7396
+ // ============================================================================
7397
+ /**
7398
+ * 🔍 Check if a transaction response requires external wallet signing
7399
+ *
7400
+ * Returns true when the user needs to sign the transaction externally
7401
+ * (e.g., via WebAuthn signer app) before it can be submitted.
7402
+ *
7403
+ * @example
7404
+ * ```typescript
7405
+ * const response = await sdk.transactions.createTransaction(request);
7406
+ * if (needsExternalSigning(response)) {
7407
+ * const signingUrl = getSigningUrl(response);
7408
+ * // Show "Click to Sign" button with signingUrl
7409
+ * }
7410
+ * ```
7411
+ */
7412
+ function needsExternalSigning(response) {
7413
+ if (!response)
7414
+ return false;
7415
+ return response.walletSigningStatus === 'WALLET_MISSING_SIGNING_ACCOUNT' ||
7416
+ response.transactionStatus === exports.TransactionStatus.PENDING_SIGNATURE;
7417
+ }
7418
+ /**
7419
+ * 🔍 Check if a transaction response is signed and ready for submission
7420
+ *
7421
+ * Returns true when the user has signed the transaction and it's waiting
7422
+ * to be submitted to the blockchain (typically by a business in POS flows).
7423
+ *
7424
+ * @example
7425
+ * ```typescript
7426
+ * const response = await sdk.transactions.getTransaction(transactionId);
7427
+ * if (needsSubmission(response)) {
7428
+ * // Transaction is signed, ready to submit
7429
+ * await sdk.transactions.submitSignedTransaction({ ... });
7430
+ * }
7431
+ * ```
7432
+ */
7433
+ function needsSubmission(response) {
7434
+ if (!response)
7435
+ return false;
7436
+ return response.walletSigningStatus === 'WALLET_HAS_SIGNED' &&
7437
+ response.transactionStatus === exports.TransactionStatus.PENDING_SUBMISSION;
7438
+ }
7439
+ /**
7440
+ * 🔗 Get the signing URL from a transaction response
7441
+ *
7442
+ * Returns the URL where the user should be redirected to sign the transaction.
7443
+ * Optionally appends `signOnly=true` for POS flows where the signer app
7444
+ * should skip the confirmation UI.
7445
+ *
7446
+ * @param response - Transaction response from API
7447
+ * @param options - Optional configuration
7448
+ * @param options.signOnly - If true, appends signOnly param for POS flows (default: false)
7449
+ * @returns Signing URL or null if not available
7450
+ *
7451
+ * @example
7452
+ * ```typescript
7453
+ * const signingUrl = getSigningUrl(response, { signOnly: isPOSTransaction });
7454
+ * if (signingUrl) {
7455
+ * window.open(signingUrl, '_blank');
7456
+ * }
7457
+ * ```
7458
+ */
7459
+ function getSigningUrl(response, options) {
7460
+ if (!response?.actionable?.actionUrl)
7461
+ return null;
7462
+ const baseUrl = response.actionable.actionUrl;
7463
+ // For POS transactions, append signOnly to skip confirmation in signer app
7464
+ if (options?.signOnly) {
7465
+ return baseUrl + '&signOnly=true';
7466
+ }
7467
+ return baseUrl;
7468
+ }
7242
7469
  // ============================================================================
7243
7470
  // CLIENT TRANSACTION TYPES
7244
7471
  // ============================================================================
@@ -7885,6 +8112,15 @@ class TenantApi {
7885
8112
  // ==========================================
7886
8113
  // ADMIN OPERATIONS
7887
8114
  // ==========================================
8115
+ /**
8116
+ * ADMIN: Get current admin info (tenant admin authentication required)
8117
+ *
8118
+ * Endpoint: GET /admins/me
8119
+ * Auth: @Admin()
8120
+ */
8121
+ async getCurrentAdmin() {
8122
+ return this.apiClient.get(`${this.adminPath}/me`);
8123
+ }
7888
8124
  /**
7889
8125
  * ADMIN: Update tenant information
7890
8126
  * UPDATED: Uses PUT /tenants (admin endpoint)
@@ -7959,6 +8195,12 @@ class TenantService {
7959
8195
  // ==========================================
7960
8196
  // ADMIN OPERATIONS
7961
8197
  // ==========================================
8198
+ /**
8199
+ * ADMIN: Get current admin info (tenant admin authentication required)
8200
+ */
8201
+ async getCurrentAdmin() {
8202
+ return this.tenantApi.getCurrentAdmin();
8203
+ }
7962
8204
  /**
7963
8205
  * ADMIN: Update tenant information
7964
8206
  * Returns full TenantDTO including sensitive config
@@ -8097,6 +8339,24 @@ class TenantManager {
8097
8339
  // ==========================================
8098
8340
  // ADMIN OPERATIONS
8099
8341
  // ==========================================
8342
+ /**
8343
+ * Admin: Get current admin info
8344
+ *
8345
+ * Retrieves the authenticated admin's profile information.
8346
+ * Requires tenant admin authentication.
8347
+ *
8348
+ * @returns Promise resolving to current admin data
8349
+ * @throws {PersApiError} When not authenticated as tenant admin
8350
+ *
8351
+ * @example
8352
+ * ```typescript
8353
+ * const admin = await sdk.tenants.getCurrentAdmin();
8354
+ * console.log('Current admin:', admin.email);
8355
+ * ```
8356
+ */
8357
+ async getCurrentAdmin() {
8358
+ return this.tenantService.getCurrentAdmin();
8359
+ }
8100
8360
  /**
8101
8361
  * Admin: Update tenant data
8102
8362
  *
@@ -8683,7 +8943,8 @@ const DEFAULT_PERS_CONFIG = {
8683
8943
  retries: 3,
8684
8944
  tokenRefreshMargin: 60, // Refresh tokens 60 seconds before expiry
8685
8945
  backgroundRefreshThreshold: 30, // Use background refresh if >30s remaining
8686
- captureWalletEvents: true // Auto-connect to wallet events after auth
8946
+ captureWalletEvents: true, // Auto-connect to wallet events after auth
8947
+ autoRestoreSession: true // Auto-restore session from stored tokens on init
8687
8948
  };
8688
8949
  /**
8689
8950
  * Build the API root URL based on config
@@ -8711,6 +8972,9 @@ function buildApiRoot(environment = 'production', version = 'v2', customApiUrl)
8711
8972
  * @internal
8712
8973
  */
8713
8974
  function buildWalletEventsWsUrl(environment = 'production', customWsUrl) {
8975
+ if (customWsUrl) {
8976
+ return customWsUrl;
8977
+ }
8714
8978
  const wsUrls = {
8715
8979
  staging: 'wss://events-staging.pers.ninja',
8716
8980
  production: 'wss://events.pers.ninja'
@@ -8747,11 +9011,28 @@ class AuthApi {
8747
9011
  }
8748
9012
  /**
8749
9013
  * Login tenant admin with JWT
9014
+ *
9015
+ * Authenticates an admin in a tenant context.
9016
+ *
9017
+ * @param jwt - Authentication token (Firebase JWT)
9018
+ * @param options - Tenant authentication options (tenantId for multi-tenant admins)
9019
+ * @returns Session response with tenant context
9020
+ * @throws MultipleContextSelectionError when tenantId is required but not provided
9021
+ *
9022
+ * @example
9023
+ * ```typescript
9024
+ * // Single tenant - auto-selects
9025
+ * const response = await authApi.loginTenantAdmin(jwt);
9026
+ *
9027
+ * // Multiple tenants - explicit selection
9028
+ * const response = await authApi.loginTenantAdmin(jwt, { tenantId: 'tenant-123' });
9029
+ * ```
8750
9030
  */
8751
- async loginTenantAdmin(jwt) {
9031
+ async loginTenantAdmin(jwt, options) {
8752
9032
  const body = {
8753
9033
  authToken: jwt,
8754
- authType: exports.AccountOwnerType.TENANT
9034
+ authType: exports.AccountOwnerType.TENANT,
9035
+ context: options?.tenantId ? { tenantId: options.tenantId } : undefined
8755
9036
  };
8756
9037
  return this.apiClient.post(`${this.basePath}/token`, body, { bypassAuth: true });
8757
9038
  }
@@ -8809,13 +9090,37 @@ class AuthApi {
8809
9090
  async refreshAccessToken(refreshToken) {
8810
9091
  return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken }, { bypassAuth: true, isRefreshRequest: true });
8811
9092
  }
9093
+ // ==========================================
9094
+ // IDENTITY VERIFICATION (for session restore)
9095
+ // Note: These duplicate calls in domain APIs (user-api, business-api)
9096
+ // but are kept here to avoid circular dependencies during initialization.
9097
+ // If endpoints change, update both locations.
9098
+ // ==========================================
9099
+ /**
9100
+ * Get current authenticated user
9101
+ */
9102
+ async getCurrentUser() {
9103
+ return this.apiClient.get('/users/me');
9104
+ }
9105
+ /**
9106
+ * Get current business context
9107
+ */
9108
+ async getCurrentBusiness() {
9109
+ return this.apiClient.get('/businesses/me');
9110
+ }
9111
+ /**
9112
+ * Get current admin context
9113
+ */
9114
+ async getCurrentAdmin() {
9115
+ return this.apiClient.get('/admins/me');
9116
+ }
8812
9117
  }
8813
9118
 
8814
9119
  /**
8815
9120
  * Authentication status enumeration
8816
9121
  * Represents the current state of the authentication system
8817
9122
  */
8818
- var AuthStatus;
9123
+ exports.AuthStatus = void 0;
8819
9124
  (function (AuthStatus) {
8820
9125
  /** User is authenticated with valid tokens */
8821
9126
  AuthStatus["AUTHENTICATED"] = "authenticated";
@@ -8831,8 +9136,14 @@ var AuthStatus;
8831
9136
  AuthStatus["SESSION_RESTORED"] = "session_restored";
8832
9137
  /** Session restoration determined no valid session exists */
8833
9138
  AuthStatus["SESSION_RESTORATION_FAILED"] = "session_restoration_failed";
8834
- })(AuthStatus || (AuthStatus = {}));
9139
+ })(exports.AuthStatus || (exports.AuthStatus = {}));
8835
9140
 
9141
+ /**
9142
+ * Type guard to check if provider implements extended storage methods
9143
+ */
9144
+ function isExtendedProvider(provider) {
9145
+ return 'setAuthType' in provider && typeof provider.setAuthType === 'function';
9146
+ }
8836
9147
  /**
8837
9148
  * Platform-agnostic authentication service
8838
9149
  * Handles login, token refresh, and storage operations
@@ -8842,7 +9153,13 @@ class AuthService {
8842
9153
  this.authApi = authApi;
8843
9154
  this.authProvider = authProvider;
8844
9155
  this.activeRefreshPromise = null;
8845
- this.currentAuthStatus = AuthStatus.UNAUTHENTICATED;
9156
+ this.currentAuthStatus = exports.AuthStatus.UNAUTHENTICATED;
9157
+ }
9158
+ /**
9159
+ * Get extended provider if available, null otherwise
9160
+ */
9161
+ get extendedProvider() {
9162
+ return this.authProvider && isExtendedProvider(this.authProvider) ? this.authProvider : null;
8846
9163
  }
8847
9164
  /**
8848
9165
  * Emit auth status change to the app
@@ -8851,9 +9168,10 @@ class AuthService {
8851
9168
  if (this.currentAuthStatus === status)
8852
9169
  return; // No change
8853
9170
  this.currentAuthStatus = status;
8854
- if (this.authProvider && typeof this.authProvider.onAuthStatusChange === 'function') {
9171
+ const extended = this.extendedProvider;
9172
+ if (extended?.onAuthStatusChange) {
8855
9173
  try {
8856
- await this.authProvider.onAuthStatusChange(status);
9174
+ await extended.onAuthStatusChange(status);
8857
9175
  }
8858
9176
  catch (error) {
8859
9177
  console.warn('[AuthService] Auth status change callback failed:', error);
@@ -8865,9 +9183,14 @@ class AuthService {
8865
9183
  // ==========================================
8866
9184
  /**
8867
9185
  * Login tenant admin with JWT
9186
+ *
9187
+ * @param jwt - Authentication token (Firebase JWT)
9188
+ * @param options - Tenant authentication options (tenantId for multi-tenant admins)
9189
+ * @returns Session response with tenant context
9190
+ * @throws Error with code MULTIPLE_CONTEXT_SELECTION_REQUIRED when tenantId is required but not provided
8868
9191
  */
8869
- async loginTenantAdmin(jwt) {
8870
- const response = await this.authApi.loginTenantAdmin(jwt);
9192
+ async loginTenantAdmin(jwt, options) {
9193
+ const response = await this.authApi.loginTenantAdmin(jwt, options);
8871
9194
  if (this.authProvider && response.accessToken) {
8872
9195
  await this.storeTokens(response.accessToken, response.refreshToken, exports.AccountOwnerType.TENANT, jwt);
8873
9196
  }
@@ -8925,7 +9248,7 @@ class AuthService {
8925
9248
  * Refresh access token with race condition protection
8926
9249
  */
8927
9250
  async refreshAccessToken(refreshToken) {
8928
- if (this.currentAuthStatus === AuthStatus.AUTH_FAILED) {
9251
+ if (this.currentAuthStatus === exports.AuthStatus.AUTH_FAILED) {
8929
9252
  throw new Error('Auth failure has occurred, cannot refresh token');
8930
9253
  }
8931
9254
  if (this.activeRefreshPromise) {
@@ -8948,9 +9271,13 @@ class AuthService {
8948
9271
  if (!tokenToUse) {
8949
9272
  throw new Error('No refresh token available for token refresh');
8950
9273
  }
9274
+ // Get the current authType to preserve it after refresh
9275
+ const extended = this.extendedProvider;
9276
+ const currentAuthType = extended?.getAuthType ? await extended.getAuthType() ?? undefined : undefined;
8951
9277
  const response = await this.authApi.refreshAccessToken(tokenToUse);
8952
9278
  if (this.authProvider && response.accessToken) {
8953
- await this.storeTokens(response.accessToken, response.refreshToken);
9279
+ // Preserve the authType during refresh
9280
+ await this.storeTokens(response.accessToken, response.refreshToken, currentAuthType);
8954
9281
  }
8955
9282
  return response;
8956
9283
  }
@@ -8968,7 +9295,7 @@ class AuthService {
8968
9295
  * Centralized method to avoid redundant token cleanup logic
8969
9296
  */
8970
9297
  async handleAuthFailure() {
8971
- if (this.currentAuthStatus === AuthStatus.AUTH_FAILED) {
9298
+ if (this.currentAuthStatus === exports.AuthStatus.AUTH_FAILED) {
8972
9299
  return;
8973
9300
  }
8974
9301
  // Clear active refresh operations to prevent hanging promises
@@ -8976,7 +9303,7 @@ class AuthService {
8976
9303
  // Clear all stored tokens
8977
9304
  await this.clearTokens();
8978
9305
  // Emit auth failure status to notify the app
8979
- await this.emitAuthStatus(AuthStatus.AUTH_FAILED);
9306
+ await this.emitAuthStatus(exports.AuthStatus.AUTH_FAILED);
8980
9307
  }
8981
9308
  /**
8982
9309
  * Check if we have valid authentication tokens
@@ -8988,6 +9315,27 @@ class AuthService {
8988
9315
  return !!token;
8989
9316
  }
8990
9317
  // ==========================================
9318
+ // CONTEXT RETRIEVAL
9319
+ // ==========================================
9320
+ /**
9321
+ * Get current authenticated user
9322
+ */
9323
+ async getCurrentUser() {
9324
+ return this.authApi.getCurrentUser();
9325
+ }
9326
+ /**
9327
+ * Get current business context
9328
+ */
9329
+ async getCurrentBusiness() {
9330
+ return this.authApi.getCurrentBusiness();
9331
+ }
9332
+ /**
9333
+ * Get current admin context
9334
+ */
9335
+ async getCurrentAdmin() {
9336
+ return this.authApi.getCurrentAdmin();
9337
+ }
9338
+ // ==========================================
8991
9339
  // PRIVATE HELPERS
8992
9340
  // ==========================================
8993
9341
  /**
@@ -9001,20 +9349,21 @@ class AuthService {
9001
9349
  if (refreshToken) {
9002
9350
  await this.authProvider.setRefreshToken(refreshToken);
9003
9351
  }
9004
- // Type-safe handling of extended token storage features
9005
- const extendedProvider = this.authProvider;
9006
- if (providerToken && extendedProvider.setProviderToken) {
9007
- await extendedProvider.setProviderToken(providerToken);
9008
- }
9009
- if (authType && extendedProvider.setAuthType) {
9010
- console.log('[AuthService] Storing auth type:', authType);
9011
- await extendedProvider.setAuthType(authType);
9352
+ // Handle optional extended storage methods
9353
+ const extended = this.extendedProvider;
9354
+ if (extended) {
9355
+ if (providerToken && extended.setProviderToken) {
9356
+ await extended.setProviderToken(providerToken);
9357
+ }
9358
+ if (authType && extended.setAuthType) {
9359
+ await extended.setAuthType(authType);
9360
+ }
9012
9361
  }
9013
- else {
9014
- console.warn('[AuthService] Auth type not stored - authType:', authType, 'has setAuthType:', !!extendedProvider.setAuthType);
9362
+ else if (authType) {
9363
+ console.warn('[AuthService] Auth type not stored - provider does not implement setAuthType');
9015
9364
  }
9016
9365
  // Emit authenticated status on successful token storage
9017
- await this.emitAuthStatus(AuthStatus.AUTHENTICATED);
9366
+ await this.emitAuthStatus(exports.AuthStatus.AUTHENTICATED);
9018
9367
  }
9019
9368
  catch (error) {
9020
9369
  console.warn('[AuthService] Failed to store tokens:', error);
@@ -9505,7 +9854,7 @@ class DefaultAuthProvider {
9505
9854
  async onAuthStatusChange(status) {
9506
9855
  try {
9507
9856
  // Handle provider-level responsibilities first
9508
- if (status === AuthStatus.TOKEN_EXPIRED && this.config.tokenProvider) {
9857
+ if (status === exports.AuthStatus.TOKEN_EXPIRED && this.config.tokenProvider) {
9509
9858
  const newToken = await this.config.tokenProvider();
9510
9859
  if (newToken) {
9511
9860
  await this.tokenManager.setAccessToken(newToken);
@@ -9930,6 +10279,108 @@ class PersApiClient {
9930
10279
  }
9931
10280
  }
9932
10281
 
10282
+ /**
10283
+ * Static JWT Authentication Provider
10284
+ * For server-side integrations with long-lived JWT tokens
10285
+ *
10286
+ * Use this provider when:
10287
+ * - Running in server environments (Node.js, serverless functions)
10288
+ * - Using static/long-lived JWT tokens (not OAuth flow)
10289
+ * - No token refresh mechanism needed
10290
+ * - JWT rotation handled externally (e.g., environment variables)
10291
+ *
10292
+ * @example Server-side usage
10293
+ * ```typescript
10294
+ * const sdk = new PersSdk(httpClient, {
10295
+ * environment: 'production',
10296
+ * apiProjectKey: 'your-project',
10297
+ * authProvider: new StaticJwtAuthProvider(
10298
+ * process.env.PERS_JWT_TOKEN!,
10299
+ * 'your-project',
10300
+ * 'business' // or 'admin'
10301
+ * )
10302
+ * });
10303
+ * ```
10304
+ */
10305
+ /**
10306
+ * Simple authentication provider for static JWT tokens
10307
+ * No token storage, refresh, or DPoP - designed for server-side use
10308
+ */
10309
+ class StaticJwtAuthProvider {
10310
+ constructor(configOrJwt, projectKey, authType) {
10311
+ if (typeof configOrJwt === 'string') {
10312
+ // Legacy constructor: (jwt, projectKey, authType)
10313
+ this.jwt = configOrJwt;
10314
+ this.projectKey = projectKey;
10315
+ this.authType = authType || exports.AccountOwnerType.BUSINESS;
10316
+ }
10317
+ else {
10318
+ // New constructor: (config)
10319
+ this.jwt = configOrJwt.jwt;
10320
+ this.projectKey = configOrJwt.projectKey;
10321
+ this.authType = configOrJwt.authType || exports.AccountOwnerType.BUSINESS;
10322
+ this.statusChangeCallback = configOrJwt.onAuthStatusChange;
10323
+ }
10324
+ if (!this.jwt) {
10325
+ throw new Error('StaticJwtAuthProvider: JWT token is required');
10326
+ }
10327
+ if (!this.projectKey) {
10328
+ throw new Error('StaticJwtAuthProvider: Project key is required');
10329
+ }
10330
+ }
10331
+ /**
10332
+ * Get the static JWT token
10333
+ * @returns The configured JWT token
10334
+ */
10335
+ async getToken() {
10336
+ return this.jwt;
10337
+ }
10338
+ /**
10339
+ * Get project key for API requests
10340
+ * @returns The configured project key
10341
+ */
10342
+ async getProjectKey() {
10343
+ return this.projectKey;
10344
+ }
10345
+ /**
10346
+ * Get authentication headers (Bearer token)
10347
+ * Note: No DPoP support for server-side static tokens
10348
+ *
10349
+ * @param token - The access token (ignored, uses configured JWT)
10350
+ * @returns Headers with Bearer authorization
10351
+ */
10352
+ async getAuthHeaders(token) {
10353
+ const actualToken = token || this.jwt;
10354
+ return actualToken ? { 'Authorization': `Bearer ${actualToken}` } : {};
10355
+ }
10356
+ /**
10357
+ * Handle authentication status changes
10358
+ *
10359
+ * Note: Static JWTs cannot be refreshed automatically.
10360
+ * TOKEN_EXPIRED status indicates manual JWT rotation is needed.
10361
+ *
10362
+ * @param status - The new authentication status
10363
+ */
10364
+ async onAuthStatusChange(status) {
10365
+ if (status === exports.AuthStatus.TOKEN_EXPIRED) {
10366
+ console.warn('[StaticJwtAuthProvider] JWT token expired. Manual rotation required. ' +
10367
+ 'Update the JWT in your environment configuration.');
10368
+ }
10369
+ // Delegate to custom callback if provided
10370
+ if (this.statusChangeCallback) {
10371
+ await this.statusChangeCallback(status);
10372
+ }
10373
+ }
10374
+ /**
10375
+ * Clear tokens (no-op for static provider)
10376
+ * Static JWTs are immutable and managed externally
10377
+ */
10378
+ async clearTokens() {
10379
+ // No-op: Static JWTs cannot be cleared, they must be rotated externally
10380
+ console.warn('[StaticJwtAuthProvider] clearTokens() called but static JWTs cannot be cleared. Rotate JWT externally.');
10381
+ }
10382
+ }
10383
+
9933
10384
  /**
9934
10385
  * PERS SDK Event Emitter
9935
10386
  *
@@ -9949,6 +10400,7 @@ function generateEventId() {
9949
10400
  *
9950
10401
  * All events flow through one subscription point.
9951
10402
  * Optional filtering at subscription level.
10403
+ * Supports replay of last auth event for late subscribers.
9952
10404
  *
9953
10405
  * @example Basic Usage
9954
10406
  * ```typescript
@@ -9972,6 +10424,13 @@ function generateEventId() {
9972
10424
  * showNotification(event.userMessage);
9973
10425
  * }, { excludeDomains: ['validation'] });
9974
10426
  *
10427
+ * // Subscribe with replay - get last auth event if missed
10428
+ * sdk.events.subscribe((event) => {
10429
+ * if (event.type === 'session_restored') {
10430
+ * setUser(event.details.user);
10431
+ * }
10432
+ * }, { domains: ['authentication'], replay: true });
10433
+ *
9975
10434
  * // Cleanup
9976
10435
  * unsubscribe();
9977
10436
  * ```
@@ -9979,6 +10438,8 @@ function generateEventId() {
9979
10438
  let emitterInstanceCounter = 0;
9980
10439
  class PersEventEmitter {
9981
10440
  constructor() {
10441
+ // Store last auth event for replay to late subscribers
10442
+ this.lastAuthEvent = null;
9982
10443
  this.handlers = new Set();
9983
10444
  this._instanceId = ++emitterInstanceCounter;
9984
10445
  }
@@ -9989,7 +10450,7 @@ class PersEventEmitter {
9989
10450
  * Subscribe to events
9990
10451
  *
9991
10452
  * @param handler - Callback for matching events
9992
- * @param filter - Optional filter by domain and/or level
10453
+ * @param filter - Optional filter by domain and/or level. Set `replay: true` to receive last auth event if missed.
9993
10454
  * @returns Unsubscribe function
9994
10455
  *
9995
10456
  * @example All events
@@ -10013,10 +10474,35 @@ class PersEventEmitter {
10013
10474
  * confetti();
10014
10475
  * }, { domains: ['transaction'], levels: ['success'] });
10015
10476
  * ```
10477
+ *
10478
+ * @example Auth events with replay (catches session_restored even if emitted before subscription)
10479
+ * ```typescript
10480
+ * sdk.events.subscribe((event) => {
10481
+ * if (event.type === 'session_restored') {
10482
+ * const { user } = event.details;
10483
+ * setUser(user);
10484
+ * }
10485
+ * }, { domains: ['authentication'], replay: true });
10486
+ * ```
10016
10487
  */
10017
10488
  subscribe(handler, filter) {
10018
10489
  const filteredHandler = { handler, filter };
10019
10490
  this.handlers.add(filteredHandler);
10491
+ // If replay is requested and we have a cached auth event, replay it immediately
10492
+ if (filter?.replay && this.lastAuthEvent) {
10493
+ // Check if event matches the filter
10494
+ if (!filter || this.matchesFilter(this.lastAuthEvent, filter)) {
10495
+ // Use queueMicrotask to ensure handler runs after subscribe returns
10496
+ queueMicrotask(() => {
10497
+ try {
10498
+ handler(this.lastAuthEvent);
10499
+ }
10500
+ catch (error) {
10501
+ console.error('[PersEventEmitter] Replay handler error:', error);
10502
+ }
10503
+ });
10504
+ }
10505
+ }
10020
10506
  return () => this.handlers.delete(filteredHandler);
10021
10507
  }
10022
10508
  /**
@@ -10041,6 +10527,7 @@ class PersEventEmitter {
10041
10527
  * Emit a success event
10042
10528
  *
10043
10529
  * Domain is restricted to business domains only (Domain type).
10530
+ * Auth events (login_success, session_restored) are cached for replay to late subscribers.
10044
10531
  *
10045
10532
  * @param event - Success event data (id and timestamp auto-generated)
10046
10533
  * @returns The complete event object
@@ -10061,6 +10548,15 @@ class PersEventEmitter {
10061
10548
  id: generateEventId(),
10062
10549
  timestamp: Date.now()
10063
10550
  };
10551
+ // Cache auth events for replay to late subscribers
10552
+ if (event.domain === 'authentication' &&
10553
+ (event.type === 'login_success' || event.type === 'session_restored')) {
10554
+ this.lastAuthEvent = fullEvent;
10555
+ }
10556
+ // Clear cached auth on logout
10557
+ if (event.domain === 'authentication' && event.type === 'logout_success') {
10558
+ this.lastAuthEvent = null;
10559
+ }
10064
10560
  this.notifyHandlers(fullEvent);
10065
10561
  return fullEvent;
10066
10562
  }
@@ -10088,6 +10584,10 @@ class PersEventEmitter {
10088
10584
  id: generateEventId(),
10089
10585
  timestamp: Date.now()
10090
10586
  };
10587
+ // Clear cached auth on auth failure (session invalid)
10588
+ if (event.domain === 'authentication' && event.type === 'session_restoration_failed') {
10589
+ this.lastAuthEvent = null;
10590
+ }
10091
10591
  this.notifyHandlers(fullEvent);
10092
10592
  return fullEvent;
10093
10593
  }
@@ -10211,7 +10711,8 @@ class AuthManager {
10211
10711
  this.events?.emitSuccess({
10212
10712
  domain: 'authentication',
10213
10713
  type: 'login_success',
10214
- userMessage: 'Successfully logged in'
10714
+ userMessage: 'Successfully logged in',
10715
+ details: { user: result.user }
10215
10716
  });
10216
10717
  return result;
10217
10718
  }
@@ -10259,7 +10760,66 @@ class AuthManager {
10259
10760
  */
10260
10761
  async loginAsBusiness(jwtToken, options) {
10261
10762
  const authService = this.apiClient.getAuthService();
10262
- return authService.loginBusiness(jwtToken, options);
10763
+ const result = await authService.loginBusiness(jwtToken, options);
10764
+ this.events?.emitSuccess({
10765
+ domain: 'authentication',
10766
+ type: 'login_success',
10767
+ userMessage: 'Successfully logged in as business',
10768
+ details: { user: result.user, business: result.business }
10769
+ });
10770
+ return result;
10771
+ }
10772
+ /**
10773
+ * Login as tenant admin with JWT token
10774
+ *
10775
+ * Authenticates an admin in a tenant context.
10776
+ *
10777
+ * **Auto-Selection Behavior:**
10778
+ * - If admin has access to a single tenant, it's auto-selected
10779
+ * - If admin has access to multiple tenants and no tenantId is provided,
10780
+ * throws `MULTIPLE_CONTEXT_SELECTION_REQUIRED` error with available options
10781
+ *
10782
+ * @param jwtToken - JWT token from auth provider (Firebase, etc.)
10783
+ * @param options - Tenant authentication options (tenantId for multi-tenant admins)
10784
+ * @returns Promise resolving to authentication response with tenant context
10785
+ * @throws Error with code `MULTIPLE_CONTEXT_SELECTION_REQUIRED` when tenantId is needed
10786
+ *
10787
+ * @example Single Tenant Access
10788
+ * ```typescript
10789
+ * // Auto-selects the admin's only tenant
10790
+ * const result = await sdk.auth.loginAsTenant(jwt);
10791
+ * console.log('Tenant:', result.admin?.tenantId);
10792
+ * ```
10793
+ *
10794
+ * @example Multiple Tenant Access
10795
+ * ```typescript
10796
+ * try {
10797
+ * const result = await sdk.auth.loginAsTenant(jwt);
10798
+ * } catch (error) {
10799
+ * if (error.code === 'MULTIPLE_CONTEXT_SELECTION_REQUIRED') {
10800
+ * // Show tenant selector UI
10801
+ * const selectedId = await showTenantSelector(error.details.availableOptions);
10802
+ * const result = await sdk.auth.loginAsTenant(jwt, { tenantId: selectedId });
10803
+ * }
10804
+ * }
10805
+ * ```
10806
+ *
10807
+ * @example Direct Tenant Selection
10808
+ * ```typescript
10809
+ * const result = await sdk.auth.loginAsTenant(jwt, { tenantId: 'tenant-123' });
10810
+ * console.log('Authenticated as admin for tenant:', result.admin?.tenantId);
10811
+ * ```
10812
+ */
10813
+ async loginAsTenant(jwtToken, options) {
10814
+ const authService = this.apiClient.getAuthService();
10815
+ const result = await authService.loginTenantAdmin(jwtToken, options);
10816
+ this.events?.emitSuccess({
10817
+ domain: 'authentication',
10818
+ type: 'login_success',
10819
+ userMessage: 'Successfully logged in as tenant admin',
10820
+ details: { admin: result.admin }
10821
+ });
10822
+ return result;
10263
10823
  }
10264
10824
  /**
10265
10825
  * Get current business context
@@ -10277,7 +10837,27 @@ class AuthManager {
10277
10837
  * ```
10278
10838
  */
10279
10839
  async getCurrentBusiness() {
10280
- return this.apiClient.get('/businesses/me');
10840
+ const authService = this.apiClient.getAuthService();
10841
+ return authService.getCurrentBusiness();
10842
+ }
10843
+ /**
10844
+ * Get current admin context
10845
+ *
10846
+ * Retrieves the current admin data if authenticated as tenant admin.
10847
+ * Requires prior tenant authentication via {@link loginAsTenant}.
10848
+ *
10849
+ * @returns Promise resolving to current admin data
10850
+ * @throws {PersApiError} When not authenticated as tenant admin
10851
+ *
10852
+ * @example
10853
+ * ```typescript
10854
+ * const admin = await sdk.auth.getCurrentAdmin();
10855
+ * console.log('Current admin:', admin.email);
10856
+ * ```
10857
+ */
10858
+ async getCurrentAdmin() {
10859
+ const authService = this.apiClient.getAuthService();
10860
+ return authService.getCurrentAdmin();
10281
10861
  }
10282
10862
  /**
10283
10863
  * Login with raw user data
@@ -10321,7 +10901,8 @@ class AuthManager {
10321
10901
  * ```
10322
10902
  */
10323
10903
  async getCurrentUser() {
10324
- return this.apiClient.get('/users/me');
10904
+ const authService = this.apiClient.getAuthService();
10905
+ return authService.getCurrentUser();
10325
10906
  }
10326
10907
  /**
10327
10908
  * Check if user is authenticated
@@ -12123,7 +12704,7 @@ class CampaignManager {
12123
12704
  * Returns campaigns with pagination metadata for efficient data loading.
12124
12705
  * Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all.
12125
12706
  *
12126
- * @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, tag, search, businessId, include)
12707
+ * @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, tag, search, businessId, startDate, endDate, include)
12127
12708
  * @returns Promise resolving to paginated campaigns with metadata
12128
12709
  *
12129
12710
  * @example
@@ -12153,18 +12734,22 @@ class CampaignManager {
12153
12734
  * active: true
12154
12735
  * });
12155
12736
  *
12156
- * // Get active campaigns sorted by creation date
12157
- * const activeCampaigns = await sdk.campaigns.getCampaigns({
12158
- * active: true,
12159
- * sortBy: 'createdAt',
12160
- * sortOrder: 'DESC',
12161
- * page: 1,
12162
- * limit: 25
12737
+ * // Filter by date range
12738
+ * const dateFiltered = await sdk.campaigns.getCampaigns({
12739
+ * startDate: new Date('2026-05-01'),
12740
+ * endDate: new Date('2026-12-31'),
12741
+ * active: true
12742
+ * });
12743
+ *
12744
+ * // Get campaigns with claim count
12745
+ * const withCounts = await sdk.campaigns.getCampaigns({
12746
+ * include: ['claimCount']
12163
12747
  * });
12748
+ * withCounts.data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
12164
12749
  *
12165
- * // Include related data (trigger sources and businesses)
12750
+ * // Include related data (trigger sources, businesses, and claim count)
12166
12751
  * const campaignsWithRelations = await sdk.campaigns.getCampaigns({
12167
- * include: ['triggerSources', 'businesses']
12752
+ * include: ['triggerSources', 'businesses', 'claimCount']
12168
12753
  * });
12169
12754
  * ```
12170
12755
  */
@@ -12615,58 +13200,52 @@ class CampaignManager {
12615
13200
  async deleteCampaignBusinessEngagement(campaignId, engagementId) {
12616
13201
  return this.campaignService.deleteCampaignBusinessEngagement(campaignId, engagementId);
12617
13202
  }
12618
- /**
12619
- * Admin: Get all campaign claims
12620
- *
12621
- * Retrieves all campaign claims across the system for comprehensive reporting
12622
- * and analytics. This operation requires administrator privileges and provides
12623
- * system-wide visibility into campaign performance and user engagement.
12624
- *
12625
- * @returns Promise resolving to array of all campaign claims
12626
- * @throws {PersApiError} When not authenticated as administrator
12627
- *
12628
- * @example
12629
- * ```typescript
12630
- * // Admin operation - get campaign performance data
12631
- * const allClaims = await sdk.campaigns.getCampaignClaims();
12632
- *
12633
- * console.log(`Campaign Performance Report:`);
12634
- * console.log(`Total claims: ${allClaims.length}`);
12635
- *
12636
- * // Analyze claims by campaign
12637
- * const claimsByCampaign = allClaims.reduce((acc, claim) => {
12638
- * const campaignId = claim.campaignId;
12639
- * acc[campaignId] = (acc[campaignId] || 0) + 1;
12640
- * return acc;
12641
- * }, {});
12642
- *
12643
- * console.log('\nClaims by Campaign:');
12644
- * Object.entries(claimsByCampaign).forEach(([campaignId, count]) => {
12645
- * console.log(`${campaignId}: ${count} claims`);
12646
- * });
12647
- * ```
12648
- */
12649
13203
  /**
12650
13204
  * Admin: Get campaign claims with optional filters
12651
13205
  *
12652
- * Retrieves campaign claims with optional filtering by campaign, user, or business.
13206
+ * Retrieves campaign claims with optional filtering by campaign, user, business, or date range.
12653
13207
  * This operation requires administrator privileges and provides system-wide
12654
13208
  * visibility into campaign performance and user engagement.
12655
13209
  *
12656
13210
  * @param filters - Optional filters and pagination options
13211
+ * @param filters.campaignId - Filter by specific campaign ID
13212
+ * @param filters.userId - Filter by specific user ID
13213
+ * @param filters.businessId - Filter by specific business ID
13214
+ * @param filters.dateFrom - Filter claims from this date (inclusive)
13215
+ * @param filters.dateTo - Filter claims until this date (inclusive)
13216
+ * @param filters.page - Page number for pagination
13217
+ * @param filters.limit - Items per page
13218
+ * @param filters.sortBy - Field to sort by
13219
+ * @param filters.sortOrder - Sort direction ('asc' or 'desc')
13220
+ * @param include - Optional relations to include for enrichment
12657
13221
  * @returns Promise resolving to paginated campaign claims
12658
13222
  * @throws {PersApiError} When not authenticated as administrator
12659
13223
  *
12660
- * @example
13224
+ * @example Get All Claims
12661
13225
  * ```typescript
12662
- * // Get all claims
12663
- * const allClaims = await sdk.campaigns.getCampaignClaims();
13226
+ * const { data: allClaims, pagination } = await sdk.campaigns.getCampaignClaims();
13227
+ * console.log(`Total: ${pagination.total}`);
13228
+ * ```
12664
13229
  *
12665
- * // Get claims for specific user
12666
- * const userClaims = await sdk.campaigns.getCampaignClaims({ userId: 'user-123' });
13230
+ * @example Filter by User
13231
+ * ```typescript
13232
+ * const { data: userClaims } = await sdk.campaigns.getCampaignClaims({ userId: 'user-123' });
13233
+ * ```
12667
13234
  *
12668
- * // Get claims for specific campaign with pagination
12669
- * const page1 = await sdk.campaigns.getCampaignClaims({
13235
+ * @example Filter by Date Range
13236
+ * ```typescript
13237
+ * // Get claims within a specific date range
13238
+ * const { data: monthClaims } = await sdk.campaigns.getCampaignClaims({
13239
+ * dateFrom: new Date('2024-01-01'),
13240
+ * dateTo: new Date('2024-01-31'),
13241
+ * limit: 100
13242
+ * });
13243
+ * console.log(`${monthClaims.length} claims in January 2024`);
13244
+ * ```
13245
+ *
13246
+ * @example With Pagination and Campaign Filter
13247
+ * ```typescript
13248
+ * const { data: page1 } = await sdk.campaigns.getCampaignClaims({
12670
13249
  * campaignId: 'campaign-456',
12671
13250
  * page: 1,
12672
13251
  * limit: 50
@@ -13271,9 +13850,19 @@ class RedemptionManager {
13271
13850
  *
13272
13851
  * Retrieves all redemption redeems across the platform with filtering capabilities.
13273
13852
  * This is an admin-level operation that allows monitoring and analytics of redemption
13274
- * activity. Supports filtering by user, redemption offer, and enrichment of related entities.
13275
- *
13276
- * @param filters - Filter options (userId, redemptionId, pagination)
13853
+ * activity. Supports filtering by user, redemption offer, date range, and enrichment of related entities.
13854
+ *
13855
+ * @param filters - Filter options (userId, redemptionId, businessId, status, dateFrom, dateTo, pagination)
13856
+ * @param filters.userId - Filter by specific user ID
13857
+ * @param filters.redemptionId - Filter by specific redemption offer ID
13858
+ * @param filters.businessId - Filter by specific business ID
13859
+ * @param filters.status - Filter by redeem status
13860
+ * @param filters.dateFrom - Filter redeems from this date (inclusive)
13861
+ * @param filters.dateTo - Filter redeems until this date (inclusive)
13862
+ * @param filters.page - Page number for pagination
13863
+ * @param filters.limit - Items per page
13864
+ * @param filters.sortBy - Field to sort by
13865
+ * @param filters.sortOrder - Sort direction ('asc' or 'desc')
13277
13866
  * @param include - Optional relations to include for enrichment
13278
13867
  * @returns Promise resolving to paginated list of redemption redeems
13279
13868
  * @throws {PersApiError} When not authenticated as admin
@@ -13298,6 +13887,18 @@ class RedemptionManager {
13298
13887
  * console.log(`User has ${userRedeems.length} redemptions`);
13299
13888
  * ```
13300
13889
  *
13890
+ * @example Filter by Date Range
13891
+ * ```typescript
13892
+ * // Get redeems within a specific date range
13893
+ * const { data: recentRedeems } = await sdk.redemptions.getRedemptionRedeems({
13894
+ * dateFrom: new Date('2024-01-01'),
13895
+ * dateTo: new Date('2024-01-31'),
13896
+ * limit: 100
13897
+ * });
13898
+ *
13899
+ * console.log(`${recentRedeems.length} redeems in January 2024`);
13900
+ * ```
13901
+ *
13301
13902
  * @example With Enriched Data
13302
13903
  * ```typescript
13303
13904
  * const { data: redeems } = await sdk.redemptions.getRedemptionRedeems(
@@ -17026,6 +17627,12 @@ class PersEventsClient {
17026
17627
  }
17027
17628
  }
17028
17629
  }
17630
+ /**
17631
+ * Create a PERS Events client instance
17632
+ */
17633
+ function createPersEventsClient(config) {
17634
+ return new PersEventsClient(config);
17635
+ }
17029
17636
 
17030
17637
  /**
17031
17638
  * Wallet Events Manager - Real-time blockchain events for user's wallets
@@ -17228,7 +17835,8 @@ class WalletEventsManager {
17228
17835
  }
17229
17836
  // Save subscriptions for auto-resubscribe on reconnect
17230
17837
  this.lastSubscriptions.wallets = wallets;
17231
- return this.client.subscribeWallets(wallets);
17838
+ const result = await this.client.subscribeWallets(wallets);
17839
+ return result;
17232
17840
  }
17233
17841
  /**
17234
17842
  * Subscribe to chain events (Admin Dashboard)
@@ -17251,7 +17859,8 @@ class WalletEventsManager {
17251
17859
  }
17252
17860
  // Save subscriptions for auto-resubscribe on reconnect
17253
17861
  this.lastSubscriptions.chains = chains;
17254
- return this.client.subscribeChains(chains);
17862
+ const result = await this.client.subscribeChains(chains);
17863
+ return result;
17255
17864
  }
17256
17865
  /**
17257
17866
  * Unsubscribe from wallet events
@@ -17561,6 +18170,24 @@ class PersSDK {
17561
18170
  this.apiClient.setEvents(this._events);
17562
18171
  // Auto-connect to wallet events on successful login (if enabled)
17563
18172
  this.setupWalletEventsAutoConnect();
18173
+ // Auto-restore session from stored tokens (if enabled, default: true)
18174
+ this.setupAutoRestoreSession();
18175
+ }
18176
+ /**
18177
+ * Setup automatic session restoration from stored tokens
18178
+ * @internal
18179
+ */
18180
+ setupAutoRestoreSession() {
18181
+ const config = this.apiClient.getConfig();
18182
+ // Check if autoRestoreSession is enabled (default: true)
18183
+ if (config.autoRestoreSession === false) {
18184
+ return;
18185
+ }
18186
+ // Fire-and-forget: restore session asynchronously
18187
+ // Events will be emitted on success/failure
18188
+ this.restoreSession().catch(() => {
18189
+ // Error already emitted via session_restoration_failed event
18190
+ });
17564
18191
  }
17565
18192
  /**
17566
18193
  * Setup auto-connect for wallet events on authentication
@@ -17638,22 +18265,23 @@ class PersSDK {
17638
18265
  /**
17639
18266
  * Restore user session from stored tokens
17640
18267
  *
17641
- * Call this method after SDK initialization to restore the user's session
17642
- * if valid tokens exist. This is useful for maintaining login state across
17643
- * app restarts.
18268
+ * **Note:** This method is called automatically on SDK initialization when
18269
+ * `autoRestoreSession` is enabled (default: true). You only need to call this
18270
+ * manually if you disabled auto-restore or need to force a session refresh.
18271
+ *
18272
+ * Validates stored tokens and fetches user data to restore the session.
18273
+ * Emits `session_restored` event on success, `session_restoration_failed` on error.
17644
18274
  *
17645
18275
  * **Important:** Only works for USER and BUSINESS accounts. Admin/tenant
17646
18276
  * accounts don't support user data fetching, so this will return null for them
17647
18277
  * (though their tokens remain valid for API calls).
17648
18278
  *
17649
- * Emits auth domain success event when session is restored.
17650
- *
17651
18279
  * @returns Promise resolving to User data if session restored, null if no session or admin account
17652
18280
  * @throws {PersError} If token validation or user fetch fails
17653
18281
  *
17654
18282
  * @example
17655
18283
  * ```typescript
17656
- * // Call after initial render for better perceived performance
18284
+ * // Manual restore (only needed if autoRestoreSession: false)
17657
18285
  * const user = await sdk.restoreSession();
17658
18286
  * if (user) {
17659
18287
  * console.log('Welcome back,', user.name);
@@ -17667,25 +18295,57 @@ class PersSDK {
17667
18295
  }
17668
18296
  // Check auth type - only restore session for user and business accounts
17669
18297
  const authProvider = this.apiClient.getConfig().authProvider;
17670
- if (authProvider?.getAuthType) {
17671
- const authType = await authProvider.getAuthType();
17672
- if (authType === exports.AccountOwnerType.TENANT) {
17673
- // Admin sessions don't support getCurrentUser(), skip restoration
17674
- // Tokens are valid and will be used for API calls
18298
+ const authType = authProvider?.getAuthType ? await authProvider.getAuthType() : exports.AccountOwnerType.USER;
18299
+ if (authType === exports.AccountOwnerType.TENANT) {
18300
+ // Ensure token is valid (refresh if expired) before fetching admin
18301
+ try {
18302
+ await this.auth.ensureValidToken();
18303
+ // Fetch current admin data
18304
+ const adminData = await this.auth.getCurrentAdmin();
18305
+ // Emit session restored for tenant admin
18306
+ this._events.emitSuccess({
18307
+ type: 'session_restored',
18308
+ domain: 'authentication',
18309
+ userMessage: 'Admin session restored successfully',
18310
+ details: { admin: adminData }
18311
+ });
17675
18312
  return null;
17676
18313
  }
18314
+ catch (error) {
18315
+ await this.auth.clearAuth();
18316
+ this._events.emitError({
18317
+ type: 'session_restoration_failed',
18318
+ domain: 'authentication',
18319
+ userMessage: 'Admin session restoration failed',
18320
+ code: 'SESSION_INVALID',
18321
+ details: { error }
18322
+ });
18323
+ throw error;
18324
+ }
17677
18325
  }
17678
18326
  try {
17679
18327
  // Ensure token is valid (refresh if expired) before fetching user
17680
18328
  await this.auth.ensureValidToken();
17681
- // Fetch user data to restore session
18329
+ // For BUSINESS auth, only fetch business context (no access to /users/me)
18330
+ if (authType === exports.AccountOwnerType.BUSINESS) {
18331
+ const businessData = await this.auth.getCurrentBusiness();
18332
+ // Emit event with business data (no user data for business auth)
18333
+ this._events.emitSuccess({
18334
+ type: 'session_restored',
18335
+ domain: 'authentication',
18336
+ userMessage: 'Business session restored successfully',
18337
+ details: { business: businessData }
18338
+ });
18339
+ return null; // No user data for business auth
18340
+ }
18341
+ // For USER auth, fetch user data
17682
18342
  const userData = await this.auth.getCurrentUser();
17683
- // Emit event through proper event emitter
18343
+ // Emit event with user data
17684
18344
  this._events.emitSuccess({
17685
18345
  type: 'session_restored',
17686
18346
  domain: 'authentication',
17687
18347
  userMessage: 'Session restored successfully',
17688
- details: { userId: userData.id }
18348
+ details: { user: userData }
17689
18349
  });
17690
18350
  return userData;
17691
18351
  }
@@ -18178,6 +18838,114 @@ class PersSDK {
18178
18838
  return !!this.apiClient.getConfig().authProvider;
18179
18839
  }
18180
18840
  }
18841
+ /**
18842
+ * Factory function for creating PERS SDK
18843
+ *
18844
+ * @param httpClient Platform-specific HTTP client implementation
18845
+ * @param config SDK configuration options
18846
+ * @returns PERS SDK instance
18847
+ */
18848
+ function createPersSDK(httpClient, config) {
18849
+ return new PersSDK(httpClient, config);
18850
+ }
18851
+
18852
+ class IndexedDBTokenStorage {
18853
+ constructor() {
18854
+ this.supportsObjects = true;
18855
+ this.dbPromise = null;
18856
+ if (typeof indexedDB === 'undefined') {
18857
+ console.warn('IndexedDB is not available in this environment');
18858
+ }
18859
+ }
18860
+ getDB() {
18861
+ if (this.dbPromise)
18862
+ return this.dbPromise;
18863
+ this.dbPromise = new Promise((resolve, reject) => {
18864
+ if (typeof indexedDB === 'undefined') {
18865
+ return reject(new Error('IndexedDB not supported'));
18866
+ }
18867
+ const request = indexedDB.open(IndexedDBTokenStorage.DB_NAME, IndexedDBTokenStorage.DB_VERSION);
18868
+ request.onupgradeneeded = (event) => {
18869
+ const db = event.target.result;
18870
+ if (!db.objectStoreNames.contains(IndexedDBTokenStorage.STORE_NAME)) {
18871
+ db.createObjectStore(IndexedDBTokenStorage.STORE_NAME);
18872
+ }
18873
+ };
18874
+ request.onsuccess = (event) => {
18875
+ resolve(event.target.result);
18876
+ };
18877
+ request.onerror = (event) => {
18878
+ reject(event.target.error);
18879
+ };
18880
+ });
18881
+ return this.dbPromise;
18882
+ }
18883
+ async get(key) {
18884
+ try {
18885
+ const db = await this.getDB();
18886
+ return new Promise((resolve, reject) => {
18887
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readonly');
18888
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
18889
+ const request = store.get(key);
18890
+ request.onsuccess = () => resolve(request.result || null);
18891
+ request.onerror = () => reject(request.error);
18892
+ });
18893
+ }
18894
+ catch (e) {
18895
+ console.warn('[IndexedDBTokenStorage] Failed to get key', key, e);
18896
+ return null;
18897
+ }
18898
+ }
18899
+ async set(key, value) {
18900
+ try {
18901
+ const db = await this.getDB();
18902
+ return new Promise((resolve, reject) => {
18903
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
18904
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
18905
+ const request = store.put(value, key);
18906
+ request.onsuccess = () => resolve();
18907
+ request.onerror = () => reject(request.error);
18908
+ });
18909
+ }
18910
+ catch (e) {
18911
+ console.error('[IndexedDBTokenStorage] Failed to set key', key, e);
18912
+ throw e;
18913
+ }
18914
+ }
18915
+ async remove(key) {
18916
+ try {
18917
+ const db = await this.getDB();
18918
+ return new Promise((resolve, reject) => {
18919
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
18920
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
18921
+ const request = store.delete(key);
18922
+ request.onsuccess = () => resolve();
18923
+ request.onerror = () => reject(request.error);
18924
+ });
18925
+ }
18926
+ catch (e) {
18927
+ console.warn('[IndexedDBTokenStorage] Failed to remove key', key, e);
18928
+ }
18929
+ }
18930
+ async clear() {
18931
+ try {
18932
+ const db = await this.getDB();
18933
+ return new Promise((resolve, reject) => {
18934
+ const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
18935
+ const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
18936
+ const request = store.clear();
18937
+ request.onsuccess = () => resolve();
18938
+ request.onerror = () => reject(request.error);
18939
+ });
18940
+ }
18941
+ catch (e) {
18942
+ console.warn('[IndexedDBTokenStorage] Failed to clear storage', e);
18943
+ }
18944
+ }
18945
+ }
18946
+ IndexedDBTokenStorage.DB_NAME = 'pers-sdk-storage';
18947
+ IndexedDBTokenStorage.STORE_NAME = 'auth-tokens';
18948
+ IndexedDBTokenStorage.DB_VERSION = 1;
18181
18949
 
18182
18950
  /**
18183
18951
  * Environment Detection Utility
@@ -18208,7 +18976,16 @@ function detectEnvironment() {
18208
18976
  /**
18209
18977
  * Global environment info (cached)
18210
18978
  */
18211
- detectEnvironment();
18979
+ const environment = detectEnvironment();
18980
+ /**
18981
+ * Warns if environment might have bundling issues
18982
+ */
18983
+ function warnIfProblematicEnvironment(feature) {
18984
+ if (environment.needsNodeStreamPolyfills) {
18985
+ console.warn(`[PERS SDK] ${feature} may require additional polyfills in web environments. ` +
18986
+ `If you encounter 'readable-stream' errors, ensure proper polyfills are configured.`);
18987
+ }
18988
+ }
18212
18989
 
18213
18990
  /**
18214
18991
  * AsyncStorage Token Storage for React Native Mobile Platforms
@@ -18941,22 +19718,12 @@ const PersSDKProvider = ({ children, config }) => {
18941
19718
  if (!sdk)
18942
19719
  return;
18943
19720
  const unsubscribe = sdk.events.subscribe((event) => {
18944
- if (event.domain !== 'authentication')
18945
- return;
18946
- // Session restored successfully - sync React state
18947
- if (event.type === 'session_restored') {
18948
- console.log('[PersSDK] Session restoration event received, syncing state...');
18949
- // Read user from event details if available, otherwise fetch
18950
- const userId = event.details?.userId;
18951
- if (userId) {
18952
- // User ID available, fetch user data
18953
- sdk.users.getCurrentUser()
18954
- .then(userData => {
18955
- setAuthenticationState(userData, true);
18956
- })
18957
- .catch(error => {
18958
- console.error('[PersSDK] Failed to sync restored session:', error);
18959
- });
19721
+ // Handle both session restoration and fresh login
19722
+ if (event.type === 'session_restored' || event.type === 'login_success') {
19723
+ const details = event.details;
19724
+ if (details?.user) {
19725
+ console.log(`[PersSDK] ${event.type}: syncing user state`);
19726
+ setAuthenticationState(details.user, true);
18960
19727
  }
18961
19728
  }
18962
19729
  // Session restoration failed or auth error - clear React state
@@ -18964,7 +19731,7 @@ const PersSDKProvider = ({ children, config }) => {
18964
19731
  console.log('[PersSDK] Authentication failed - clearing session');
18965
19732
  setAuthenticationState(null, false);
18966
19733
  }
18967
- }, { domains: ['authentication'] });
19734
+ }, { domains: ['authentication'], replay: true });
18968
19735
  return () => {
18969
19736
  unsubscribe();
18970
19737
  };
@@ -19090,21 +19857,15 @@ const useAuth = () => {
19090
19857
  // Set token in auth provider
19091
19858
  await authProvider.setAccessToken(jwtToken);
19092
19859
  // Perform login using the manager
19860
+ // SDK emits login_success event with user data - PersSDKProvider handles state sync
19093
19861
  const result = await sdk.auth.loginWithToken(jwtToken, userType);
19094
- const userData = result.user || result.admin;
19095
- if (userData) {
19096
- setAuthenticationState(userData, true);
19097
- return result;
19098
- }
19099
- else {
19100
- throw new Error('No user data returned from login');
19101
- }
19862
+ return result;
19102
19863
  }
19103
19864
  catch (error) {
19104
19865
  console.error('Login failed:', error);
19105
19866
  throw error;
19106
19867
  }
19107
- }, [sdk, authProvider, setAuthenticationState]);
19868
+ }, [sdk, authProvider]);
19108
19869
  /**
19109
19870
  * Logs in a user using raw user data (direct authentication bypass)
19110
19871
  *
@@ -19123,24 +19884,18 @@ const useAuth = () => {
19123
19884
  }
19124
19885
  try {
19125
19886
  // Use the raw data login from the auth manager
19887
+ // SDK emits login_success event with user data - PersSDKProvider handles state sync
19126
19888
  const result = await sdk.auth.loginWithRawData(rawUserData);
19127
19889
  // Set token from result
19128
19890
  if (result.accessToken) {
19129
19891
  await authProvider.setAccessToken(result.accessToken);
19130
19892
  }
19131
- const userData = result.user;
19132
- if (userData) {
19133
- setAuthenticationState(userData, true);
19134
- }
19135
- else {
19136
- throw new Error('No user data returned from raw data login');
19137
- }
19138
19893
  }
19139
19894
  catch (error) {
19140
19895
  console.error('Raw data login failed:', error);
19141
19896
  throw error;
19142
19897
  }
19143
- }, [sdk, authProvider, setAuthenticationState]);
19898
+ }, [sdk, authProvider]);
19144
19899
  /**
19145
19900
  * Logs out the current user and clears authentication state
19146
19901
  *
@@ -40275,6 +41030,581 @@ const useWeb3 = () => {
40275
41030
  };
40276
41031
  };
40277
41032
 
41033
+ /**
41034
+ * Token SDK - Class-based Promise SDK for Token Operations
41035
+ *
41036
+ * Modern, performant SDK with direct method access and excellent TypeScript support.
41037
+ * Optimized for bundle size, performance, and developer experience.
41038
+ *
41039
+ * Usage:
41040
+ * const tokenSDK = new TokenSDK(apiClient);
41041
+ * const tokens = await tokenSDK.getTokens();
41042
+ * const creditToken = await tokenSDK.getActiveCreditToken();
41043
+ */
41044
+ class TokenSDK {
41045
+ constructor(apiClient) {
41046
+ this.tokenApi = new TokenApi(apiClient);
41047
+ this.tokenService = new TokenService(this.tokenApi);
41048
+ }
41049
+ // ==========================================
41050
+ // CONVENIENCE METHODS - Direct Promise API
41051
+ // ==========================================
41052
+ // Simplified method names for better developer experience
41053
+ /**
41054
+ * Get all tokens
41055
+ * @returns Promise with all available tokens
41056
+ */
41057
+ async getTokens() {
41058
+ return this.tokenService.getRemoteTokens();
41059
+ }
41060
+ /**
41061
+ * Get all token types
41062
+ * @returns Promise with all available token types
41063
+ */
41064
+ async getTokenTypes() {
41065
+ return this.tokenService.getRemoteTokenTypes();
41066
+ }
41067
+ /**
41068
+ * Get the active credit token
41069
+ * @returns Promise with the current active credit token
41070
+ */
41071
+ async getActiveCreditToken() {
41072
+ return this.tokenService.getRemoteActiveCreditToken();
41073
+ }
41074
+ /**
41075
+ * Get all reward tokens
41076
+ * @returns Promise with all reward tokens
41077
+ */
41078
+ async getRewardTokens() {
41079
+ return this.tokenService.getRemoteRewardTokens();
41080
+ }
41081
+ /**
41082
+ * Get all status tokens
41083
+ * @returns Promise with all status tokens
41084
+ */
41085
+ async getStatusTokens() {
41086
+ return this.tokenService.getRemoteStatusTokens();
41087
+ }
41088
+ /**
41089
+ * Get token by contract address
41090
+ * @param contractAddress - The contract address to search for
41091
+ * @param contractTokenId - Optional contract token ID
41092
+ * @returns Promise with the matching token
41093
+ */
41094
+ async getTokenByContract(contractAddress, contractTokenId = null) {
41095
+ return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
41096
+ }
41097
+ // ==========================================
41098
+ // ADMIN METHODS - Token Management
41099
+ // ==========================================
41100
+ /**
41101
+ * Create token metadata
41102
+ * @param tokenId - The token ID
41103
+ * @param tokenData - The token metadata request data
41104
+ * @returns Promise with the updated token
41105
+ */
41106
+ async createTokenMetadata(tokenId, tokenData) {
41107
+ return this.tokenService.createTokenMetadata(tokenId, tokenData);
41108
+ }
41109
+ /**
41110
+ * Update token metadata (ERC721 only)
41111
+ * Note: Existing minted NFTs retain their original metadata - this only affects future mints
41112
+ * @param metadataId - The metadata ID to update
41113
+ * @param tokenData - The token metadata request data
41114
+ * @returns Promise with the updated metadata
41115
+ */
41116
+ async updateTokenMetadata(metadataId, tokenData) {
41117
+ return this.tokenService.updateTokenMetadata(metadataId, tokenData);
41118
+ }
41119
+ /**
41120
+ * Toggle token active status
41121
+ * @param tokenId - The token ID to toggle
41122
+ * @returns Promise with the updated token
41123
+ */
41124
+ async toggleTokenActive(tokenId) {
41125
+ return this.tokenService.toggleTokenActive(tokenId);
41126
+ }
41127
+ /**
41128
+ * Create a new token
41129
+ * @param tokenData - The token creation data
41130
+ * @returns Promise with the created token
41131
+ */
41132
+ async createToken(tokenData) {
41133
+ return this.tokenService.createToken(tokenData);
41134
+ }
41135
+ /**
41136
+ * Update an existing token
41137
+ * @param tokenId - The token ID to update
41138
+ * @param tokenData - The token update data
41139
+ * @returns Promise with the updated token
41140
+ */
41141
+ async updateToken(tokenId, tokenData) {
41142
+ return this.tokenService.updateToken(tokenId, tokenData);
41143
+ }
41144
+ /**
41145
+ * Set mainnet contract address for a token
41146
+ * @param tokenId - The token ID
41147
+ * @param contractAddress - The contract address
41148
+ * @param chainId - The blockchain chain ID
41149
+ * @returns Promise with the updated token
41150
+ */
41151
+ async setMainnetContract(tokenId, contractAddress, chainId) {
41152
+ return this.tokenService.setMainnetContract(tokenId, contractAddress, chainId);
41153
+ }
41154
+ /**
41155
+ * Toggle token metadata status
41156
+ * @param metadataId - The metadata ID to toggle
41157
+ * @returns Promise with the updated metadata
41158
+ */
41159
+ async toggleTokenMetadataStatus(metadataId) {
41160
+ return this.tokenService.toggleTokenMetadataStatus(metadataId);
41161
+ }
41162
+ /**
41163
+ * Create a new token type
41164
+ * @param tokenType - The token type data
41165
+ * @returns Promise with the created token type
41166
+ */
41167
+ async createTokenType(tokenType) {
41168
+ return this.tokenService.createTokenType(tokenType);
41169
+ }
41170
+ // ==========================================
41171
+ // ADVANCED ACCESS - For Complex Operations
41172
+ // ==========================================
41173
+ /**
41174
+ * Get direct access to the token service for advanced operations
41175
+ * @returns The underlying TokenService instance
41176
+ */
41177
+ getTokenService() {
41178
+ return this.tokenService;
41179
+ }
41180
+ /**
41181
+ * Get direct access to the token API for low-level operations
41182
+ * @returns The underlying TokenApi instance
41183
+ */
41184
+ getTokenApi() {
41185
+ return this.tokenApi;
41186
+ }
41187
+ }
41188
+
41189
+ // ==========================================
41190
+ // TOKEN UTILITY FUNCTIONS
41191
+ // ==========================================
41192
+ /**
41193
+ * Get metadata from a token unit response
41194
+ * Handles metadata-based tokens (ERC-1155 with metadata array)
41195
+ *
41196
+ * For metadata-based tokens:
41197
+ * - Returns the metadata object from token.metadata array
41198
+ * - Uses provided incrementalId or tokenMetadataIncrementalId from tokenUnit
41199
+ * - Defaults to index 0 if no incrementalId specified
41200
+ *
41201
+ * For standard tokens (no metadata):
41202
+ * - Returns null
41203
+ *
41204
+ * @param tokenUnit - The token unit from campaign or balance
41205
+ * @param incrementalId - Optional metadata index to use (overrides tokenUnit.tokenMetadataIncrementalId)
41206
+ * @returns Token metadata object or null
41207
+ *
41208
+ * @example
41209
+ * ```typescript
41210
+ * // Get metadata using tokenUnit's incrementalId
41211
+ * const tokenUnit = {
41212
+ * tokenMetadataIncrementalId: 3,
41213
+ * token: {
41214
+ * metadata: [
41215
+ * { name: 'Bronze Pass', imageUrl: '...' },
41216
+ * { name: 'Silver Pass', imageUrl: '...' },
41217
+ * { name: 'Gold Pass', imageUrl: '...' }
41218
+ * ]
41219
+ * }
41220
+ * };
41221
+ * const metadata = getMetadataFromTokenUnitResponse(tokenUnit);
41222
+ * // Returns: { name: 'Bronze Pass', imageUrl: '...' } (index 0 - default)
41223
+ *
41224
+ * // Override incrementalId
41225
+ * const metadata2 = getMetadataFromTokenUnitResponse(tokenUnit, 2);
41226
+ * // Returns: { name: 'Gold Pass', imageUrl: '...' } (index 2)
41227
+ *
41228
+ * // Standard token (no metadata)
41229
+ * const tokenUnit2 = {
41230
+ * token: { symbol: 'VQP', name: 'Visit Qatar Points' }
41231
+ * };
41232
+ * const metadata3 = getMetadataFromTokenUnitResponse(tokenUnit2);
41233
+ * // Returns: null
41234
+ *
41235
+ * // Display name with fallback
41236
+ * const displayName = metadata?.name || tokenUnit.token?.symbol || 'Reward';
41237
+ *
41238
+ * // Display image
41239
+ * const imageUrl = metadata?.imageUrl;
41240
+ * ```
41241
+ */
41242
+ function getMetadataFromTokenUnitResponse(tokenUnit, incrementalId) {
41243
+ // If no metadata array exists, return null
41244
+ if (!tokenUnit.token?.metadata || tokenUnit.token.metadata.length === 0) {
41245
+ return null;
41246
+ }
41247
+ const metadata = tokenUnit.token.metadata;
41248
+ // Use provided incrementalId or tokenUnit's incrementalId to find matching metadata
41249
+ const targetId = incrementalId ?? tokenUnit.tokenMetadataIncrementalId;
41250
+ if (targetId !== undefined) {
41251
+ const found = metadata.find((m) => m.incrementalId === targetId);
41252
+ if (found)
41253
+ return found;
41254
+ }
41255
+ // Fallback to first metadata item
41256
+ return metadata[0];
41257
+ }
41258
+
41259
+ /**
41260
+ * Abstract Base Token Service - Explicit Initialization Pattern
41261
+ *
41262
+ * Platform-agnostic token operations with Promise-based API.
41263
+ * Framework services extend this and control initialization lifecycle.
41264
+ *
41265
+ * Benefits:
41266
+ * - Explicit initialization control
41267
+ * - Better error boundaries
41268
+ * - Clear lifecycle management
41269
+ * - Testable initialization state
41270
+ * - Zero framework dependencies
41271
+ */
41272
+ class BaseTokenService {
41273
+ constructor() {
41274
+ this._isInitialized = false;
41275
+ }
41276
+ // ==========================================
41277
+ // INITIALIZATION LIFECYCLE
41278
+ // ==========================================
41279
+ /**
41280
+ * LIFECYCLE: Initialize token service with API client
41281
+ * Must be called before using any token operations
41282
+ */
41283
+ initializeTokenService(apiClient) {
41284
+ if (!apiClient) {
41285
+ throw new Error('Cannot initialize TokenService: apiClient is null or undefined');
41286
+ }
41287
+ if (!this._isInitialized) {
41288
+ this._tokenApi = new TokenApi(apiClient);
41289
+ this._tokenBusinessService = new TokenService(this._tokenApi);
41290
+ this._isInitialized = true;
41291
+ }
41292
+ }
41293
+ /**
41294
+ * LIFECYCLE: Check if token service is initialized
41295
+ */
41296
+ get isTokenServiceInitialized() {
41297
+ return this._isInitialized;
41298
+ }
41299
+ /**
41300
+ * INTERNAL: Get token business service (throws if not initialized)
41301
+ */
41302
+ get tokenBusinessService() {
41303
+ if (!this._tokenBusinessService) {
41304
+ throw new Error('TokenService not initialized. Call initializeTokenService(apiClient) first.');
41305
+ }
41306
+ return this._tokenBusinessService;
41307
+ }
41308
+ // ==========================================
41309
+ // PUBLIC OPERATIONS
41310
+ // ==========================================
41311
+ /**
41312
+ * PUBLIC: Get all remote tokens
41313
+ */
41314
+ getRemoteTokens() {
41315
+ return this.tokenBusinessService.getRemoteTokens();
41316
+ }
41317
+ /**
41318
+ * PUBLIC: Get all remote token types
41319
+ */
41320
+ getRemoteTokenTypes() {
41321
+ return this.tokenBusinessService.getRemoteTokenTypes();
41322
+ }
41323
+ /**
41324
+ * PUBLIC: Get active credit token
41325
+ */
41326
+ getRemoteActiveCreditToken() {
41327
+ return this.tokenBusinessService.getRemoteActiveCreditToken();
41328
+ }
41329
+ /**
41330
+ * PUBLIC: Get reward tokens
41331
+ */
41332
+ getRemoteRewardTokens() {
41333
+ return this.tokenBusinessService.getRemoteRewardTokens();
41334
+ }
41335
+ /**
41336
+ * PUBLIC: Get status tokens
41337
+ */
41338
+ getRemoteStatusTokens() {
41339
+ return this.tokenBusinessService.getRemoteStatusTokens();
41340
+ }
41341
+ /**
41342
+ * PUBLIC: Get token by contract address
41343
+ */
41344
+ getTokenByContractAddress(contractAddress, contractTokenId) {
41345
+ return this.tokenBusinessService.getTokenByContractAddress(contractAddress, contractTokenId);
41346
+ }
41347
+ // ==========================================
41348
+ // ADMIN OPERATIONS
41349
+ // ==========================================
41350
+ /**
41351
+ * ADMIN: Create token metadata
41352
+ */
41353
+ createTokenMetadata(tokenId, tokenData) {
41354
+ return this.tokenBusinessService.createTokenMetadata(tokenId, tokenData);
41355
+ }
41356
+ /**
41357
+ * ADMIN: Toggle token active status
41358
+ */
41359
+ toggleTokenActive(tokenId) {
41360
+ return this.tokenBusinessService.toggleTokenActive(tokenId);
41361
+ }
41362
+ /**
41363
+ * ADMIN: Create new token
41364
+ */
41365
+ createToken(tokenData) {
41366
+ return this.tokenBusinessService.createToken(tokenData);
41367
+ }
41368
+ /**
41369
+ * ADMIN: Update token
41370
+ */
41371
+ updateToken(tokenId, tokenData) {
41372
+ return this.tokenBusinessService.updateToken(tokenId, tokenData);
41373
+ }
41374
+ /**
41375
+ * ADMIN: Set mainnet contract address
41376
+ */
41377
+ setMainnetContract(tokenId, contractAddress, chainId) {
41378
+ return this.tokenBusinessService.setMainnetContract(tokenId, contractAddress, chainId);
41379
+ }
41380
+ /**
41381
+ * ADMIN: Toggle token metadata status
41382
+ */
41383
+ toggleTokenMetadataStatus(metadataId) {
41384
+ return this.tokenBusinessService.toggleTokenMetadataStatus(metadataId);
41385
+ }
41386
+ /**
41387
+ * ADMIN: Create token type
41388
+ */
41389
+ createTokenType(tokenType) {
41390
+ return this.tokenBusinessService.createTokenType(tokenType);
41391
+ }
41392
+ }
41393
+
41394
+ /**
41395
+ * Node.js HTTP Client adapter for PERS SDK
41396
+ *
41397
+ * Simple HTTP client for Node.js environments using native fetch (Node 18+) or a polyfill.
41398
+ * Works with Express, Next.js API routes, or any Node.js-based backend.
41399
+ *
41400
+ * @example
41401
+ * ```typescript
41402
+ * import { createPersSDK } from '@explorins/pers-sdk';
41403
+ * import { NodeHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
41404
+ *
41405
+ * const sdk = createPersSDK(
41406
+ * new NodeHttpClientAdapter(),
41407
+ * { environment: 'production', apiProjectKey: 'your-key' }
41408
+ * );
41409
+ *
41410
+ * // Use managers directly
41411
+ * await sdk.user.getCurrentUser();
41412
+ * ```
41413
+ */
41414
+ class NodeHttpClientAdapter {
41415
+ async get(url, options = {}) {
41416
+ return this.request(url, 'GET', undefined, options);
41417
+ }
41418
+ async post(url, data = null, options = {}) {
41419
+ return this.request(url, 'POST', data, options);
41420
+ }
41421
+ async put(url, data = null, options = {}) {
41422
+ return this.request(url, 'PUT', data, options);
41423
+ }
41424
+ async delete(url, options = {}) {
41425
+ return this.request(url, 'DELETE', undefined, options);
41426
+ }
41427
+ async patch(url, data = null, options = {}) {
41428
+ return this.request(url, 'PATCH', data, options);
41429
+ }
41430
+ async request(url, method, data, options = {}) {
41431
+ // Use native fetch in Node 18+ or require a polyfill for older versions
41432
+ const fetchImpl = globalThis.fetch || require('node-fetch');
41433
+ const headers = {
41434
+ 'Content-Type': 'application/json',
41435
+ 'User-Agent': 'PERS-SDK-Node/1.0',
41436
+ ...options.headers
41437
+ };
41438
+ const fetchOptions = {
41439
+ method,
41440
+ headers,
41441
+ ...options
41442
+ };
41443
+ if (data !== undefined && method !== 'GET') {
41444
+ fetchOptions.body = JSON.stringify(data);
41445
+ }
41446
+ const response = await fetchImpl(url, fetchOptions);
41447
+ if (!response.ok) {
41448
+ // Extract backend error response if available
41449
+ let errorData;
41450
+ try {
41451
+ const contentType = response.headers.get('content-type');
41452
+ if (contentType?.includes('application/json')) {
41453
+ errorData = await response.json();
41454
+ }
41455
+ }
41456
+ catch {
41457
+ // If parsing fails, continue with basic error
41458
+ }
41459
+ // Create error with backend data attached
41460
+ const error = new Error(errorData?.message || `HTTP ${response.status}: ${response.statusText}`);
41461
+ error.status = response.status;
41462
+ error.statusText = response.statusText;
41463
+ // Attach backend error response for extraction
41464
+ if (errorData) {
41465
+ Object.assign(error, errorData);
41466
+ }
41467
+ throw error;
41468
+ }
41469
+ // Handle different response types
41470
+ const contentType = response.headers.get('content-type');
41471
+ if (contentType?.includes('application/json')) {
41472
+ return response.json();
41473
+ }
41474
+ return response.text();
41475
+ }
41476
+ }
41477
+
41478
+ /**
41479
+ * Angular HTTP Client adapter for PERS SDK
41480
+ *
41481
+ * Simple bridge between Angular's HttpClient and PERS SDK's HttpClient interface.
41482
+ * Use this for easy integration with Angular applications without requiring Angular as a dependency.
41483
+ *
41484
+ * @example
41485
+ * ```typescript
41486
+ * import { inject } from '@angular/core';
41487
+ * import { HttpClient } from '@angular/common/http';
41488
+ * import { createPersSDK } from '@explorins/pers-sdk';
41489
+ * import { AngularHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
41490
+ *
41491
+ * const sdk = createPersSDK(
41492
+ * new AngularHttpClientAdapter(inject(HttpClient)),
41493
+ * { environment: 'production', apiProjectKey: 'your-key' }
41494
+ * );
41495
+ *
41496
+ * // Use managers directly
41497
+ * await sdk.user.getCurrentUser();
41498
+ * ```
41499
+ */
41500
+ class AngularHttpClientAdapter {
41501
+ constructor(httpClient) {
41502
+ this.httpClient = httpClient;
41503
+ }
41504
+ async get(url, options = {}) {
41505
+ const mergedOptions = this.mergeOptions(options);
41506
+ const request$ = this.httpClient.get(url, mergedOptions);
41507
+ return this.convertObservableToPromise(request$);
41508
+ }
41509
+ async post(url, data = null, options = {}) {
41510
+ const mergedOptions = this.mergeOptions(options);
41511
+ const request$ = this.httpClient.post(url, data, mergedOptions);
41512
+ return this.convertObservableToPromise(request$);
41513
+ }
41514
+ async put(url, data = null, options = {}) {
41515
+ const mergedOptions = this.mergeOptions(options);
41516
+ const request$ = this.httpClient.put(url, data, mergedOptions);
41517
+ return this.convertObservableToPromise(request$);
41518
+ }
41519
+ async delete(url, options = {}) {
41520
+ const mergedOptions = this.mergeOptions(options);
41521
+ const request$ = this.httpClient.delete(url, mergedOptions);
41522
+ return this.convertObservableToPromise(request$);
41523
+ }
41524
+ async patch(url, data = null, options = {}) {
41525
+ const mergedOptions = this.mergeOptions(options);
41526
+ const request$ = this.httpClient.patch(url, data, mergedOptions);
41527
+ return this.convertObservableToPromise(request$);
41528
+ }
41529
+ async convertObservableToPromise(observable) {
41530
+ // Dynamically import firstValueFrom to avoid build-time dependency
41531
+ const { firstValueFrom } = await Promise.resolve().then(function () { return require('./index-CzLxqePZ.js'); }).then(function (n) { return n.index; });
41532
+ return firstValueFrom(observable);
41533
+ }
41534
+ mergeOptions(options) {
41535
+ // Create headers object without importing HttpHeaders
41536
+ const defaultHeaders = { 'Content-Type': 'application/json' };
41537
+ const headers = { ...defaultHeaders, ...options.headers };
41538
+ return {
41539
+ headers,
41540
+ observe: 'body',
41541
+ ...options
41542
+ };
41543
+ }
41544
+ }
41545
+
41546
+ /**
41547
+ * Browser Fetch HTTP Client adapter for PERS SDK
41548
+ *
41549
+ * Simple fetch-based HTTP client for browser environments.
41550
+ * Works with vanilla JavaScript, React, Vue, or any browser-based framework.
41551
+ *
41552
+ * @example
41553
+ * ```typescript
41554
+ * import { createPersSDK } from '@explorins/pers-sdk';
41555
+ * import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
41556
+ *
41557
+ * const sdk = createPersSDK(
41558
+ * new BrowserFetchClientAdapter(),
41559
+ * { environment: 'production', apiProjectKey: 'your-key' }
41560
+ * );
41561
+ *
41562
+ * // Use managers directly
41563
+ * await sdk.user.getCurrentUser();
41564
+ * ```
41565
+ */
41566
+ class BrowserFetchClientAdapter {
41567
+ async get(url, options = {}) {
41568
+ return this.request(url, 'GET', undefined, options);
41569
+ }
41570
+ async post(url, data = null, options = {}) {
41571
+ return this.request(url, 'POST', data, options);
41572
+ }
41573
+ async put(url, data = null, options = {}) {
41574
+ return this.request(url, 'PUT', data, options);
41575
+ }
41576
+ async delete(url, options = {}) {
41577
+ return this.request(url, 'DELETE', undefined, options);
41578
+ }
41579
+ async patch(url, data = null, options = {}) {
41580
+ return this.request(url, 'PATCH', data, options);
41581
+ }
41582
+ async request(url, method, data, options = {}) {
41583
+ const headers = {
41584
+ 'Content-Type': 'application/json',
41585
+ ...options.headers
41586
+ };
41587
+ const fetchOptions = {
41588
+ method,
41589
+ headers,
41590
+ ...options
41591
+ };
41592
+ if (data !== undefined && method !== 'GET') {
41593
+ fetchOptions.body = JSON.stringify(data);
41594
+ }
41595
+ const response = await fetch(url, fetchOptions);
41596
+ if (!response.ok) {
41597
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
41598
+ }
41599
+ // Handle different response types
41600
+ const contentType = response.headers.get('content-type');
41601
+ if (contentType?.includes('application/json')) {
41602
+ return response.json();
41603
+ }
41604
+ return response.text();
41605
+ }
41606
+ }
41607
+
40278
41608
  /**
40279
41609
  * React hook for loading and managing token balances
40280
41610
  *
@@ -41438,12 +42768,38 @@ const useBusiness = () => {
41438
42768
  throw error;
41439
42769
  }
41440
42770
  }, [sdk, isInitialized]);
41441
- const getBusinesses = react.useCallback(async () => {
42771
+ /**
42772
+ * Admin: Get all businesses with optional filtering
42773
+ *
42774
+ * @param options - Filter and pagination options
42775
+ * @returns Promise resolving to paginated businesses
42776
+ *
42777
+ * @example
42778
+ * ```typescript
42779
+ * // Get all businesses
42780
+ * const { data } = await getBusinesses();
42781
+ *
42782
+ * // Filter by tag
42783
+ * const tagged = await getBusinesses({ tag: 'restaurant' });
42784
+ *
42785
+ * // Multiple tags (OR match)
42786
+ * const multiTag = await getBusinesses({ tag: ['restaurant', 'cafe'] });
42787
+ *
42788
+ * // With search and pagination
42789
+ * const filtered = await getBusinesses({
42790
+ * search: 'coffee',
42791
+ * active: true,
42792
+ * page: 1,
42793
+ * limit: 20
42794
+ * });
42795
+ * ```
42796
+ */
42797
+ const getBusinesses = react.useCallback(async (options) => {
41442
42798
  if (!isInitialized || !sdk) {
41443
42799
  throw new Error('SDK not initialized. Call initialize() first.');
41444
42800
  }
41445
42801
  try {
41446
- const result = await sdk.businesses.getBusinesses();
42802
+ const result = await sdk.businesses.getBusinesses(options);
41447
42803
  return result;
41448
42804
  }
41449
42805
  catch (error) {
@@ -41565,11 +42921,15 @@ const useCampaigns = () => {
41565
42921
  * include: ['triggerSources', 'businesses']
41566
42922
  * });
41567
42923
  *
41568
- * // Filter by tag
41569
- * const { data } = await getActiveCampaigns({ tag: 'summer-promo' });
42924
+ * // Filter by date range
42925
+ * const { data } = await getActiveCampaigns({
42926
+ * startDate: '2026-05-01',
42927
+ * endDate: '2026-12-31'
42928
+ * });
41570
42929
  *
41571
- * // Filter by business
41572
- * const { data } = await getActiveCampaigns({ businessId: 'business-123' });
42930
+ * // Get campaigns with claim count
42931
+ * const { data } = await getActiveCampaigns({ include: ['claimCount'] });
42932
+ * data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
41573
42933
  * ```
41574
42934
  */
41575
42935
  const getActiveCampaigns = react.useCallback(async (options) => {
@@ -41592,7 +42952,7 @@ const useCampaigns = () => {
41592
42952
  * Get campaign by ID with optional include relations
41593
42953
  *
41594
42954
  * @param campaignId - The campaign ID
41595
- * @param include - Relations to include: 'triggerSources', 'businesses'
42955
+ * @param include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
41596
42956
  * @returns Promise resolving to campaign data
41597
42957
  *
41598
42958
  * @example
@@ -41600,6 +42960,10 @@ const useCampaigns = () => {
41600
42960
  * // Get campaign with all related data
41601
42961
  * const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
41602
42962
  * console.log('Trigger sources:', campaign.included?.triggerSources);
42963
+ *
42964
+ * // Get campaign with claim count
42965
+ * const campaign = await getCampaignById('campaign-123', ['claimCount']);
42966
+ * console.log('Total claims:', campaign.claimCount);
41603
42967
  * ```
41604
42968
  */
41605
42969
  const getCampaignById = react.useCallback(async (campaignId, include) => {
@@ -41751,8 +43115,13 @@ const useCampaigns = () => {
41751
43115
  * // Get only active campaigns with trigger sources
41752
43116
  * const { data } = await getAllCampaigns({ active: true, include: ['triggerSources'] });
41753
43117
  *
41754
- * // Get inactive campaigns
41755
- * const { data } = await getAllCampaigns({ active: false });
43118
+ * // Filter by date range with claim count
43119
+ * const { data } = await getAllCampaigns({
43120
+ * startDate: '2026-05-01',
43121
+ * endDate: '2026-12-31',
43122
+ * include: ['claimCount']
43123
+ * });
43124
+ * data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
41756
43125
  *
41757
43126
  * // With pagination and sorting
41758
43127
  * const { data } = await getAllCampaigns({
@@ -41779,7 +43148,16 @@ const useCampaigns = () => {
41779
43148
  /**
41780
43149
  * Admin: Get campaign claims with optional filters and include relations
41781
43150
  *
41782
- * @param filters - Optional filters for campaign, user, or business
43151
+ * @param filters - Optional filters for campaign, user, business, and date range
43152
+ * @param filters.campaignId - Filter by specific campaign ID
43153
+ * @param filters.userId - Filter by user ID
43154
+ * @param filters.businessId - Filter by business ID
43155
+ * @param filters.dateFrom - Filter claims created on or after this date (ISO 8601 format)
43156
+ * @param filters.dateTo - Filter claims created on or before this date (ISO 8601 format)
43157
+ * @param filters.page - Page number (1-indexed)
43158
+ * @param filters.limit - Items per page
43159
+ * @param filters.sortBy - Field to sort by
43160
+ * @param filters.sortOrder - Sort direction
41783
43161
  * @param include - Relations to include: 'campaign', 'user', 'business'
41784
43162
  * @returns Promise resolving to paginated campaign claims
41785
43163
  *
@@ -41790,6 +43168,12 @@ const useCampaigns = () => {
41790
43168
  *
41791
43169
  * // Get claims for a specific campaign
41792
43170
  * const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
43171
+ *
43172
+ * // Filter by date range
43173
+ * const { data } = await getCampaignClaims({
43174
+ * dateFrom: '2026-01-01',
43175
+ * dateTo: '2026-03-31'
43176
+ * }, ['campaign']);
41793
43177
  * ```
41794
43178
  */
41795
43179
  const getCampaignClaims = react.useCallback(async (filters, include) => {
@@ -41945,6 +43329,9 @@ const useRedemptions = () => {
41945
43329
  * @param options - Filter and pagination options
41946
43330
  * @param options.active - Filter by active status (Admin only)
41947
43331
  * @param options.search - Free text search across name, description, tags
43332
+ * @param options.startDate - Filter redemptions starting on or after this date
43333
+ * @param options.endDate - Filter redemptions ending on or before this date
43334
+ * @param options.include - Relations to include: 'redeemCount'
41948
43335
  * @param options.page - Page number (1-indexed)
41949
43336
  * @param options.limit - Results per page
41950
43337
  * @param options.sortBy - Field to sort by ('name' | 'createdAt')
@@ -41955,12 +43342,21 @@ const useRedemptions = () => {
41955
43342
  * const { data: offers } = await getRedemptions();
41956
43343
  * ```
41957
43344
  *
41958
- * @example Search redemptions
43345
+ * @example Filter by date range
43346
+ * ```typescript
43347
+ * const { data: dateFiltered } = await getRedemptions({
43348
+ * startDate: '2026-05-01',
43349
+ * endDate: '2026-06-30'
43350
+ * });
43351
+ * ```
43352
+ *
43353
+ * @example Get redemptions with redeem count
41959
43354
  * ```typescript
41960
- * const { data: coffeeOffers } = await getRedemptions({ search: 'coffee' });
43355
+ * const { data: offers } = await getRedemptions({ include: ['redeemCount'] });
43356
+ * offers.forEach(o => console.log(`${o.name}: ${o.redeemCount} redeems`));
41961
43357
  * ```
41962
43358
  *
41963
- * @example With pagination and sorting
43359
+ * @example Search with pagination
41964
43360
  * ```typescript
41965
43361
  * const { data, pagination } = await getRedemptions({
41966
43362
  * search: 'discount',
@@ -42125,7 +43521,17 @@ const useRedemptions = () => {
42125
43521
  *
42126
43522
  * Retrieves all redemption redeems across the platform with filtering.
42127
43523
  *
42128
- * @param filters - Optional filters for user, redemption, and pagination
43524
+ * @param filters - Optional filters for user, redemption, date range, and pagination
43525
+ * @param filters.userId - Filter by user ID
43526
+ * @param filters.redemptionId - Filter by redemption offer ID
43527
+ * @param filters.businessId - Filter by business ID (admin only)
43528
+ * @param filters.status - Filter by processing status (PENDING, PROCESSING, COMPLETED, FAILED)
43529
+ * @param filters.dateFrom - Filter redeems created on or after this date (ISO 8601 format)
43530
+ * @param filters.dateTo - Filter redeems created on or before this date (ISO 8601 format)
43531
+ * @param filters.page - Page number (1-indexed)
43532
+ * @param filters.limit - Items per page
43533
+ * @param filters.sortBy - Field to sort by
43534
+ * @param filters.sortOrder - Sort direction
42129
43535
  * @param include - Relations to include: 'redemption', 'user', 'business'
42130
43536
  * @returns Promise resolving to paginated redemption redeems
42131
43537
  *
@@ -42139,6 +43545,12 @@ const useRedemptions = () => {
42139
43545
  * { userId: 'user-123' },
42140
43546
  * ['redemption']
42141
43547
  * );
43548
+ *
43549
+ * // Filter by date range
43550
+ * const { data } = await getRedemptionRedeems({
43551
+ * dateFrom: '2026-01-01',
43552
+ * dateTo: '2026-03-31'
43553
+ * }, ['user', 'redemption']);
42142
43554
  * ```
42143
43555
  */
42144
43556
  const getRedemptionRedeems = react.useCallback(async (filters, include) => {
@@ -43621,112 +45033,125 @@ const useTriggerSources = () => {
43621
45033
  };
43622
45034
  };
43623
45035
 
43624
- // ==========================================
43625
- // TOKEN UTILITY FUNCTIONS
43626
- // ==========================================
43627
- /**
43628
- * Get metadata from a token unit response
43629
- * Handles metadata-based tokens (ERC-1155 with metadata array)
43630
- *
43631
- * For metadata-based tokens:
43632
- * - Returns the metadata object from token.metadata array
43633
- * - Uses provided incrementalId or tokenMetadataIncrementalId from tokenUnit
43634
- * - Defaults to index 0 if no incrementalId specified
43635
- *
43636
- * For standard tokens (no metadata):
43637
- * - Returns null
43638
- *
43639
- * @param tokenUnit - The token unit from campaign or balance
43640
- * @param incrementalId - Optional metadata index to use (overrides tokenUnit.tokenMetadataIncrementalId)
43641
- * @returns Token metadata object or null
43642
- *
43643
- * @example
43644
- * ```typescript
43645
- * // Get metadata using tokenUnit's incrementalId
43646
- * const tokenUnit = {
43647
- * tokenMetadataIncrementalId: 3,
43648
- * token: {
43649
- * metadata: [
43650
- * { name: 'Bronze Pass', imageUrl: '...' },
43651
- * { name: 'Silver Pass', imageUrl: '...' },
43652
- * { name: 'Gold Pass', imageUrl: '...' }
43653
- * ]
43654
- * }
43655
- * };
43656
- * const metadata = getMetadataFromTokenUnitResponse(tokenUnit);
43657
- * // Returns: { name: 'Bronze Pass', imageUrl: '...' } (index 0 - default)
43658
- *
43659
- * // Override incrementalId
43660
- * const metadata2 = getMetadataFromTokenUnitResponse(tokenUnit, 2);
43661
- * // Returns: { name: 'Gold Pass', imageUrl: '...' } (index 2)
43662
- *
43663
- * // Standard token (no metadata)
43664
- * const tokenUnit2 = {
43665
- * token: { symbol: 'VQP', name: 'Visit Qatar Points' }
43666
- * };
43667
- * const metadata3 = getMetadataFromTokenUnitResponse(tokenUnit2);
43668
- * // Returns: null
43669
- *
43670
- * // Display name with fallback
43671
- * const displayName = metadata?.name || tokenUnit.token?.symbol || 'Reward';
43672
- *
43673
- * // Display image
43674
- * const imageUrl = metadata?.imageUrl;
43675
- * ```
43676
- */
43677
- function getMetadataFromTokenUnitResponse(tokenUnit, incrementalId) {
43678
- // If no metadata array exists, return null
43679
- if (!tokenUnit.token?.metadata || tokenUnit.token.metadata.length === 0) {
43680
- return null;
43681
- }
43682
- const metadata = tokenUnit.token.metadata;
43683
- // Use provided incrementalId or tokenUnit's incrementalId to find matching metadata
43684
- const targetId = incrementalId ?? tokenUnit.tokenMetadataIncrementalId;
43685
- if (targetId !== undefined) {
43686
- const found = metadata.find((m) => m.incrementalId === targetId);
43687
- if (found)
43688
- return found;
43689
- }
43690
- // Fallback to first metadata item
43691
- return metadata[0];
43692
- }
43693
-
43694
45036
  exports.ANALYTICS_GROUP_BY_TYPES = ANALYTICS_GROUP_BY_TYPES;
43695
45037
  exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
45038
+ exports.AUTH_STORAGE_KEYS = AUTH_STORAGE_KEYS;
45039
+ exports.AnalyticsApi = AnalyticsApi;
45040
+ exports.AnalyticsManager = AnalyticsManager;
45041
+ exports.AnalyticsService = AnalyticsService;
45042
+ exports.AngularHttpClientAdapter = AngularHttpClientAdapter;
45043
+ exports.ApiErrorDetector = ApiErrorDetector;
45044
+ exports.ApiKeyApi = ApiKeyApi;
45045
+ exports.ApiKeyManager = ApiKeyManager;
43696
45046
  exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
43697
45047
  exports.AuditEntityTypes = AuditEntityTypes;
45048
+ exports.AuthApi = AuthApi;
45049
+ exports.AuthManager = AuthManager;
45050
+ exports.AuthService = AuthService;
45051
+ exports.AuthTokenManager = AuthTokenManager;
43698
45052
  exports.AuthenticationError = AuthenticationError;
45053
+ exports.BaseTokenService = BaseTokenService;
45054
+ exports.BrowserFetchClientAdapter = BrowserFetchClientAdapter;
45055
+ exports.BusinessApi = BusinessApi;
45056
+ exports.BusinessManager = BusinessManager;
45057
+ exports.BusinessMembershipApi = BusinessMembershipApi;
45058
+ exports.BusinessMembershipService = BusinessMembershipService;
45059
+ exports.BusinessService = BusinessService;
45060
+ exports.CacheService = CacheService;
45061
+ exports.CacheTTL = CacheTTL;
45062
+ exports.CampaignApi = CampaignApi;
45063
+ exports.CampaignManager = CampaignManager;
45064
+ exports.CampaignService = CampaignService;
43699
45065
  exports.ChainTypes = ChainTypes;
43700
45066
  exports.ClientTransactionType = ClientTransactionType;
45067
+ exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
45068
+ exports.DPOP_STORAGE_KEYS = DPOP_STORAGE_KEYS;
45069
+ exports.DPoPManager = DPoPManager;
45070
+ exports.DefaultAuthProvider = DefaultAuthProvider;
43701
45071
  exports.Domains = Domains;
45072
+ exports.DonationApi = DonationApi;
45073
+ exports.DonationManager = DonationManager;
45074
+ exports.DonationService = DonationService;
43702
45075
  exports.EntityTypes = EntityTypes;
43703
45076
  exports.ErrorDomains = ErrorDomains;
43704
45077
  exports.ErrorUtils = ErrorUtils;
45078
+ exports.FileApi = FileApi;
45079
+ exports.FileManager = FileManager;
45080
+ exports.FileService = FileService;
45081
+ exports.IndexedDBTokenStorage = IndexedDBTokenStorage;
45082
+ exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
45083
+ exports.LogoutRequired = LogoutRequired;
43705
45084
  exports.MEMBERSHIP_ROLE_HIERARCHY = MEMBERSHIP_ROLE_HIERARCHY;
45085
+ exports.MemoryTokenStorage = MemoryTokenStorage;
43706
45086
  exports.NativeTokenTypes = NativeTokenTypes;
45087
+ exports.NetworkError = NetworkError;
45088
+ exports.NodeHttpClientAdapter = NodeHttpClientAdapter;
45089
+ exports.PaymentApi = PurchaseApi;
45090
+ exports.PaymentService = PaymentService;
45091
+ exports.PersApiClient = PersApiClient;
43707
45092
  exports.PersApiError = PersApiError;
45093
+ exports.PersEventEmitter = PersEventEmitter;
45094
+ exports.PersEventsClient = PersEventsClient;
45095
+ exports.PersSDK = PersSDK;
43708
45096
  exports.PersSDKProvider = PersSDKProvider;
45097
+ exports.ProviderTokenRefreshNeeded = ProviderTokenRefreshNeeded;
45098
+ exports.PurchaseManager = PurchaseManager;
43709
45099
  exports.QUERY_OPERATOR_SQL_MAP = QUERY_OPERATOR_SQL_MAP;
43710
45100
  exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
43711
45101
  exports.ReactNativeHttpClient = ReactNativeHttpClient;
43712
45102
  exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
45103
+ exports.RedemptionApi = RedemptionApi;
45104
+ exports.RedemptionManager = RedemptionManager;
45105
+ exports.RedemptionService = RedemptionService;
45106
+ exports.SDK_NAME = SDK_NAME;
45107
+ exports.SDK_USER_AGENT = SDK_USER_AGENT;
45108
+ exports.SDK_VERSION = SDK_VERSION;
43713
45109
  exports.SOURCE_LOGIC_TYPES = SOURCE_LOGIC_TYPES;
43714
45110
  exports.SOURCE_LOGIC_TYPE_VALUES = SOURCE_LOGIC_TYPE_VALUES;
45111
+ exports.SdkErrorCodes = SdkErrorCodes;
43715
45112
  exports.SigningStatus = SigningStatus;
45113
+ exports.StaticJwtAuthProvider = StaticJwtAuthProvider;
43716
45114
  exports.TOKEN_VALIDITY_TYPE_VALUES = TOKEN_VALIDITY_TYPE_VALUES;
43717
45115
  exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
43718
45116
  exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
43719
45117
  exports.TRIGGER_SOURCE_TYPES = TRIGGER_SOURCE_TYPES;
43720
45118
  exports.TRIGGER_SOURCE_TYPE_VALUES = TRIGGER_SOURCE_TYPE_VALUES;
45119
+ exports.TenantApi = TenantApi;
45120
+ exports.TenantManager = TenantManager;
45121
+ exports.TenantService = TenantService;
45122
+ exports.TokenApi = TokenApi;
45123
+ exports.TokenManager = TokenManager;
45124
+ exports.TokenRefreshNeeded = TokenRefreshNeeded;
45125
+ exports.TokenSDK = TokenSDK;
45126
+ exports.TokenService = TokenService;
43721
45127
  exports.TokenValidityTypes = TokenValidityTypes;
45128
+ exports.TransactionApi = TransactionApi;
45129
+ exports.TransactionManager = TransactionManager;
45130
+ exports.TransactionService = TransactionService;
45131
+ exports.TriggerSourceApi = TriggerSourceApi;
45132
+ exports.TriggerSourceManager = TriggerSourceManager;
45133
+ exports.TriggerSourceService = TriggerSourceService;
45134
+ exports.UserApi = UserApi;
45135
+ exports.UserManager = UserManager;
45136
+ exports.UserService = UserService;
45137
+ exports.UserStatusApi = UserStatusApi;
45138
+ exports.UserStatusManager = UserStatusManager;
45139
+ exports.UserStatusService = UserStatusService;
43722
45140
  exports.VALID_BUSINESS_MEMBERSHIP_RELATIONS = VALID_BUSINESS_MEMBERSHIP_RELATIONS;
43723
45141
  exports.VALID_CAMPAIGN_CLAIM_RELATIONS = VALID_CAMPAIGN_CLAIM_RELATIONS;
43724
45142
  exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
43725
45143
  exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
45144
+ exports.VALID_REDEMPTION_RELATIONS = VALID_REDEMPTION_RELATIONS;
43726
45145
  exports.VALID_RELATIONS = VALID_RELATIONS;
43727
45146
  exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
43728
45147
  exports.VALID_USER_RELATIONS = VALID_USER_RELATIONS;
45148
+ exports.WalletEventsManager = WalletEventsManager;
45149
+ exports.WebDPoPCryptoProvider = WebDPoPCryptoProvider;
45150
+ exports.WebhookApi = WebhookApi;
45151
+ exports.WebhookManager = WebhookManager;
45152
+ exports.WebhookService = WebhookService;
43729
45153
  exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;
45154
+ exports.buildApiRoot = buildApiRoot;
43730
45155
  exports.buildBurnRequest = buildBurnRequest;
43731
45156
  exports.buildMintRequest = buildMintRequest;
43732
45157
  exports.buildPOSBurnRequest = buildPOSBurnRequest;
@@ -43735,14 +45160,22 @@ exports.buildPaginationParams = buildPaginationParams;
43735
45160
  exports.buildPendingTransactionData = buildPendingTransactionData;
43736
45161
  exports.buildSubmissionRequest = buildSubmissionRequest;
43737
45162
  exports.buildTransferRequest = buildTransferRequest;
45163
+ exports.buildWalletEventsWsUrl = buildWalletEventsWsUrl;
45164
+ exports.createPersEventsClient = createPersEventsClient;
45165
+ exports.createPersSDK = createPersSDK;
43738
45166
  exports.createReactNativeAuthProvider = createReactNativeAuthProvider;
43739
45167
  exports.createUserIdentifierObject = createUserIdentifierObject;
45168
+ exports.createUserStatusSDK = createUserStatusSDK;
45169
+ exports.detectEnvironment = detectEnvironment;
43740
45170
  exports.detectIdentifierType = detectIdentifierType;
45171
+ exports.environment = environment;
43741
45172
  exports.extractData = extractData;
43742
45173
  exports.extractDeadlineFromSigningData = extractDeadlineFromSigningData;
43743
45174
  exports.extractPagination = extractPagination;
43744
45175
  exports.fetchAllPages = fetchAllPages;
43745
45176
  exports.getMetadataFromTokenUnitResponse = getMetadataFromTokenUnitResponse;
45177
+ exports.getSigningUrl = getSigningUrl;
45178
+ exports.globalCacheService = globalCacheService;
43746
45179
  exports.hasMinimumRole = hasMinimumRole;
43747
45180
  exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
43748
45181
  exports.isConnectedMessage = isConnectedMessage;
@@ -43758,9 +45191,14 @@ exports.isValidBusinessMembershipRelation = isValidBusinessMembershipRelation;
43758
45191
  exports.isValidCampaignClaimRelation = isValidCampaignClaimRelation;
43759
45192
  exports.isValidCampaignRelation = isValidCampaignRelation;
43760
45193
  exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
45194
+ exports.isValidRedemptionRelation = isValidRedemptionRelation;
43761
45195
  exports.isValidRelation = isValidRelation;
43762
45196
  exports.isValidTransactionRelation = isValidTransactionRelation;
43763
45197
  exports.isValidUserRelation = isValidUserRelation;
45198
+ exports.mergeWithDefaults = mergeWithDefaults;
45199
+ exports.needsExternalSigning = needsExternalSigning;
45200
+ exports.needsSubmission = needsSubmission;
45201
+ exports.normalizeFilters = normalizeFilters;
43764
45202
  exports.normalizeToPaginated = normalizeToPaginated;
43765
45203
  exports.registerIdentifierType = registerIdentifierType;
43766
45204
  exports.testnetPrefix = testnetPrefix;
@@ -43785,4 +45223,5 @@ exports.useTriggerSources = useTriggerSources;
43785
45223
  exports.useUserStatus = useUserStatus;
43786
45224
  exports.useUsers = useUsers;
43787
45225
  exports.useWeb3 = useWeb3;
45226
+ exports.warnIfProblematicEnvironment = warnIfProblematicEnvironment;
43788
45227
  //# sourceMappingURL=index.js.map