@explorins/pers-sdk-react-native 2.1.14-beta.0 → 2.1.18
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.
- package/dist/hooks/index.d.ts +3 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useAnalytics.d.ts +1 -1
- package/dist/hooks/useAnalytics.d.ts.map +1 -1
- package/dist/hooks/useAuth.d.ts +3 -3
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +14 -27
- package/dist/hooks/useBusiness.d.ts +3 -2
- package/dist/hooks/useBusiness.d.ts.map +1 -1
- package/dist/hooks/useBusiness.js +28 -2
- package/dist/hooks/useCampaigns.d.ts +10 -4
- package/dist/hooks/useCampaigns.d.ts.map +1 -1
- package/dist/hooks/useCampaigns.js +36 -8
- package/dist/hooks/useDonations.d.ts +1 -1
- package/dist/hooks/useDonations.d.ts.map +1 -1
- package/dist/hooks/usePurchases.d.ts +2 -2
- package/dist/hooks/usePurchases.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.d.ts +10 -2
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +32 -4
- package/dist/hooks/useTenants.d.ts +1 -1
- package/dist/hooks/useTenants.d.ts.map +1 -1
- package/dist/hooks/useTokenBalances.d.ts +1 -1
- package/dist/hooks/useTokenBalances.d.ts.map +1 -1
- package/dist/hooks/useTokenBalances.js +1 -1
- package/dist/hooks/useTokens.d.ts +1 -1
- package/dist/hooks/useTokens.d.ts.map +1 -1
- package/dist/hooks/useTransactions.d.ts +1 -1
- package/dist/hooks/useTransactions.d.ts.map +1 -1
- package/dist/hooks/useTriggerSources.d.ts +1 -1
- package/dist/hooks/useTriggerSources.d.ts.map +1 -1
- package/dist/hooks/useUserStatus.d.ts +1 -1
- package/dist/hooks/useUserStatus.d.ts.map +1 -1
- package/dist/hooks/useUsers.d.ts +1 -1
- package/dist/hooks/useUsers.d.ts.map +1 -1
- package/dist/index-CzLxqePZ.js +7717 -0
- package/dist/index-CzLxqePZ.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1731 -284
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +26 -37
- package/package.json +182 -182
- package/src/hooks/index.ts +5 -2
- package/src/hooks/useAnalytics.ts +1 -1
- package/src/hooks/useAuth.ts +16 -32
- package/src/hooks/useBusiness.ts +32 -3
- package/src/hooks/useCampaigns.ts +47 -12
- package/src/hooks/useDonations.ts +1 -1
- package/src/hooks/usePurchases.ts +1 -1
- package/src/hooks/useRedemptions.ts +43 -6
- package/src/hooks/useTenants.ts +1 -1
- package/src/hooks/useTokenBalances.ts +1 -1
- package/src/hooks/useTokens.ts +1 -1
- package/src/hooks/useTransactions.ts +1 -1
- package/src/hooks/useTriggerSources.ts +1 -1
- package/src/hooks/useUserStatus.ts +1 -1
- package/src/hooks/useUsers.ts +1 -1
- package/src/index.ts +6 -3
- package/src/providers/PersSDKProvider.tsx +29 -40
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 =
|
|
3874
|
-
this.domain =
|
|
3875
|
-
this.category =
|
|
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 =
|
|
3885
|
-
this.domain =
|
|
3886
|
-
this.category =
|
|
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 =
|
|
3895
|
-
this.domain =
|
|
3896
|
-
this.category =
|
|
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 ||
|
|
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:
|
|
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
|
|
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
|
|
5927
|
-
* const
|
|
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
|
-
|
|
5938
|
-
|
|
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
|
-
* @
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9171
|
+
const extended = this.extendedProvider;
|
|
9172
|
+
if (extended?.onAuthStatusChange) {
|
|
8855
9173
|
try {
|
|
8856
|
-
await
|
|
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
|
-
|
|
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
|
-
//
|
|
9005
|
-
const
|
|
9006
|
-
if (
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
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 -
|
|
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);
|
|
@@ -9556,6 +9905,11 @@ class DefaultAuthProvider {
|
|
|
9556
9905
|
async hasValidToken() {
|
|
9557
9906
|
return this.tokenManager.hasAccessToken();
|
|
9558
9907
|
}
|
|
9908
|
+
async setAuthType(authType) {
|
|
9909
|
+
// Auth type is derived from JWT, so this is a no-op
|
|
9910
|
+
// The authType in JWT is the source of truth
|
|
9911
|
+
// This method exists to satisfy the ExtendedTokenStorageProvider interface
|
|
9912
|
+
}
|
|
9559
9913
|
async getAuthType() {
|
|
9560
9914
|
// Read from JWT instead of separate storage for single source of truth
|
|
9561
9915
|
const token = await this.tokenManager.getAccessToken();
|
|
@@ -9930,6 +10284,108 @@ class PersApiClient {
|
|
|
9930
10284
|
}
|
|
9931
10285
|
}
|
|
9932
10286
|
|
|
10287
|
+
/**
|
|
10288
|
+
* Static JWT Authentication Provider
|
|
10289
|
+
* For server-side integrations with long-lived JWT tokens
|
|
10290
|
+
*
|
|
10291
|
+
* Use this provider when:
|
|
10292
|
+
* - Running in server environments (Node.js, serverless functions)
|
|
10293
|
+
* - Using static/long-lived JWT tokens (not OAuth flow)
|
|
10294
|
+
* - No token refresh mechanism needed
|
|
10295
|
+
* - JWT rotation handled externally (e.g., environment variables)
|
|
10296
|
+
*
|
|
10297
|
+
* @example Server-side usage
|
|
10298
|
+
* ```typescript
|
|
10299
|
+
* const sdk = new PersSdk(httpClient, {
|
|
10300
|
+
* environment: 'production',
|
|
10301
|
+
* apiProjectKey: 'your-project',
|
|
10302
|
+
* authProvider: new StaticJwtAuthProvider(
|
|
10303
|
+
* process.env.PERS_JWT_TOKEN!,
|
|
10304
|
+
* 'your-project',
|
|
10305
|
+
* 'business' // or 'admin'
|
|
10306
|
+
* )
|
|
10307
|
+
* });
|
|
10308
|
+
* ```
|
|
10309
|
+
*/
|
|
10310
|
+
/**
|
|
10311
|
+
* Simple authentication provider for static JWT tokens
|
|
10312
|
+
* No token storage, refresh, or DPoP - designed for server-side use
|
|
10313
|
+
*/
|
|
10314
|
+
class StaticJwtAuthProvider {
|
|
10315
|
+
constructor(configOrJwt, projectKey, authType) {
|
|
10316
|
+
if (typeof configOrJwt === 'string') {
|
|
10317
|
+
// Legacy constructor: (jwt, projectKey, authType)
|
|
10318
|
+
this.jwt = configOrJwt;
|
|
10319
|
+
this.projectKey = projectKey;
|
|
10320
|
+
this.authType = authType || exports.AccountOwnerType.BUSINESS;
|
|
10321
|
+
}
|
|
10322
|
+
else {
|
|
10323
|
+
// New constructor: (config)
|
|
10324
|
+
this.jwt = configOrJwt.jwt;
|
|
10325
|
+
this.projectKey = configOrJwt.projectKey;
|
|
10326
|
+
this.authType = configOrJwt.authType || exports.AccountOwnerType.BUSINESS;
|
|
10327
|
+
this.statusChangeCallback = configOrJwt.onAuthStatusChange;
|
|
10328
|
+
}
|
|
10329
|
+
if (!this.jwt) {
|
|
10330
|
+
throw new Error('StaticJwtAuthProvider: JWT token is required');
|
|
10331
|
+
}
|
|
10332
|
+
if (!this.projectKey) {
|
|
10333
|
+
throw new Error('StaticJwtAuthProvider: Project key is required');
|
|
10334
|
+
}
|
|
10335
|
+
}
|
|
10336
|
+
/**
|
|
10337
|
+
* Get the static JWT token
|
|
10338
|
+
* @returns The configured JWT token
|
|
10339
|
+
*/
|
|
10340
|
+
async getToken() {
|
|
10341
|
+
return this.jwt;
|
|
10342
|
+
}
|
|
10343
|
+
/**
|
|
10344
|
+
* Get project key for API requests
|
|
10345
|
+
* @returns The configured project key
|
|
10346
|
+
*/
|
|
10347
|
+
async getProjectKey() {
|
|
10348
|
+
return this.projectKey;
|
|
10349
|
+
}
|
|
10350
|
+
/**
|
|
10351
|
+
* Get authentication headers (Bearer token)
|
|
10352
|
+
* Note: No DPoP support for server-side static tokens
|
|
10353
|
+
*
|
|
10354
|
+
* @param token - The access token (ignored, uses configured JWT)
|
|
10355
|
+
* @returns Headers with Bearer authorization
|
|
10356
|
+
*/
|
|
10357
|
+
async getAuthHeaders(token) {
|
|
10358
|
+
const actualToken = token || this.jwt;
|
|
10359
|
+
return actualToken ? { 'Authorization': `Bearer ${actualToken}` } : {};
|
|
10360
|
+
}
|
|
10361
|
+
/**
|
|
10362
|
+
* Handle authentication status changes
|
|
10363
|
+
*
|
|
10364
|
+
* Note: Static JWTs cannot be refreshed automatically.
|
|
10365
|
+
* TOKEN_EXPIRED status indicates manual JWT rotation is needed.
|
|
10366
|
+
*
|
|
10367
|
+
* @param status - The new authentication status
|
|
10368
|
+
*/
|
|
10369
|
+
async onAuthStatusChange(status) {
|
|
10370
|
+
if (status === exports.AuthStatus.TOKEN_EXPIRED) {
|
|
10371
|
+
console.warn('[StaticJwtAuthProvider] JWT token expired. Manual rotation required. ' +
|
|
10372
|
+
'Update the JWT in your environment configuration.');
|
|
10373
|
+
}
|
|
10374
|
+
// Delegate to custom callback if provided
|
|
10375
|
+
if (this.statusChangeCallback) {
|
|
10376
|
+
await this.statusChangeCallback(status);
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
/**
|
|
10380
|
+
* Clear tokens (no-op for static provider)
|
|
10381
|
+
* Static JWTs are immutable and managed externally
|
|
10382
|
+
*/
|
|
10383
|
+
async clearTokens() {
|
|
10384
|
+
// No-op: Static JWTs cannot be cleared, they must be rotated externally
|
|
10385
|
+
console.warn('[StaticJwtAuthProvider] clearTokens() called but static JWTs cannot be cleared. Rotate JWT externally.');
|
|
10386
|
+
}
|
|
10387
|
+
}
|
|
10388
|
+
|
|
9933
10389
|
/**
|
|
9934
10390
|
* PERS SDK Event Emitter
|
|
9935
10391
|
*
|
|
@@ -9949,6 +10405,7 @@ function generateEventId() {
|
|
|
9949
10405
|
*
|
|
9950
10406
|
* All events flow through one subscription point.
|
|
9951
10407
|
* Optional filtering at subscription level.
|
|
10408
|
+
* Supports replay of last auth event for late subscribers.
|
|
9952
10409
|
*
|
|
9953
10410
|
* @example Basic Usage
|
|
9954
10411
|
* ```typescript
|
|
@@ -9972,6 +10429,13 @@ function generateEventId() {
|
|
|
9972
10429
|
* showNotification(event.userMessage);
|
|
9973
10430
|
* }, { excludeDomains: ['validation'] });
|
|
9974
10431
|
*
|
|
10432
|
+
* // Subscribe with replay - get last auth event if missed
|
|
10433
|
+
* sdk.events.subscribe((event) => {
|
|
10434
|
+
* if (event.type === 'session_restored') {
|
|
10435
|
+
* setUser(event.details.user);
|
|
10436
|
+
* }
|
|
10437
|
+
* }, { domains: ['authentication'], replay: true });
|
|
10438
|
+
*
|
|
9975
10439
|
* // Cleanup
|
|
9976
10440
|
* unsubscribe();
|
|
9977
10441
|
* ```
|
|
@@ -9979,6 +10443,8 @@ function generateEventId() {
|
|
|
9979
10443
|
let emitterInstanceCounter = 0;
|
|
9980
10444
|
class PersEventEmitter {
|
|
9981
10445
|
constructor() {
|
|
10446
|
+
// Store last auth event for replay to late subscribers
|
|
10447
|
+
this.lastAuthEvent = null;
|
|
9982
10448
|
this.handlers = new Set();
|
|
9983
10449
|
this._instanceId = ++emitterInstanceCounter;
|
|
9984
10450
|
}
|
|
@@ -9989,7 +10455,7 @@ class PersEventEmitter {
|
|
|
9989
10455
|
* Subscribe to events
|
|
9990
10456
|
*
|
|
9991
10457
|
* @param handler - Callback for matching events
|
|
9992
|
-
* @param filter - Optional filter by domain and/or level
|
|
10458
|
+
* @param filter - Optional filter by domain and/or level. Set `replay: true` to receive last auth event if missed.
|
|
9993
10459
|
* @returns Unsubscribe function
|
|
9994
10460
|
*
|
|
9995
10461
|
* @example All events
|
|
@@ -10013,10 +10479,35 @@ class PersEventEmitter {
|
|
|
10013
10479
|
* confetti();
|
|
10014
10480
|
* }, { domains: ['transaction'], levels: ['success'] });
|
|
10015
10481
|
* ```
|
|
10482
|
+
*
|
|
10483
|
+
* @example Auth events with replay (catches session_restored even if emitted before subscription)
|
|
10484
|
+
* ```typescript
|
|
10485
|
+
* sdk.events.subscribe((event) => {
|
|
10486
|
+
* if (event.type === 'session_restored') {
|
|
10487
|
+
* const { user } = event.details;
|
|
10488
|
+
* setUser(user);
|
|
10489
|
+
* }
|
|
10490
|
+
* }, { domains: ['authentication'], replay: true });
|
|
10491
|
+
* ```
|
|
10016
10492
|
*/
|
|
10017
10493
|
subscribe(handler, filter) {
|
|
10018
10494
|
const filteredHandler = { handler, filter };
|
|
10019
10495
|
this.handlers.add(filteredHandler);
|
|
10496
|
+
// If replay is requested and we have a cached auth event, replay it immediately
|
|
10497
|
+
if (filter?.replay && this.lastAuthEvent) {
|
|
10498
|
+
// Check if event matches the filter
|
|
10499
|
+
if (!filter || this.matchesFilter(this.lastAuthEvent, filter)) {
|
|
10500
|
+
// Use queueMicrotask to ensure handler runs after subscribe returns
|
|
10501
|
+
queueMicrotask(() => {
|
|
10502
|
+
try {
|
|
10503
|
+
handler(this.lastAuthEvent);
|
|
10504
|
+
}
|
|
10505
|
+
catch (error) {
|
|
10506
|
+
console.error('[PersEventEmitter] Replay handler error:', error);
|
|
10507
|
+
}
|
|
10508
|
+
});
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10020
10511
|
return () => this.handlers.delete(filteredHandler);
|
|
10021
10512
|
}
|
|
10022
10513
|
/**
|
|
@@ -10041,6 +10532,7 @@ class PersEventEmitter {
|
|
|
10041
10532
|
* Emit a success event
|
|
10042
10533
|
*
|
|
10043
10534
|
* Domain is restricted to business domains only (Domain type).
|
|
10535
|
+
* Auth events (login_success, session_restored) are cached for replay to late subscribers.
|
|
10044
10536
|
*
|
|
10045
10537
|
* @param event - Success event data (id and timestamp auto-generated)
|
|
10046
10538
|
* @returns The complete event object
|
|
@@ -10061,6 +10553,15 @@ class PersEventEmitter {
|
|
|
10061
10553
|
id: generateEventId(),
|
|
10062
10554
|
timestamp: Date.now()
|
|
10063
10555
|
};
|
|
10556
|
+
// Cache auth events for replay to late subscribers
|
|
10557
|
+
if (event.domain === 'authentication' &&
|
|
10558
|
+
(event.type === 'login_success' || event.type === 'session_restored')) {
|
|
10559
|
+
this.lastAuthEvent = fullEvent;
|
|
10560
|
+
}
|
|
10561
|
+
// Clear cached auth on logout
|
|
10562
|
+
if (event.domain === 'authentication' && event.type === 'logout_success') {
|
|
10563
|
+
this.lastAuthEvent = null;
|
|
10564
|
+
}
|
|
10064
10565
|
this.notifyHandlers(fullEvent);
|
|
10065
10566
|
return fullEvent;
|
|
10066
10567
|
}
|
|
@@ -10088,6 +10589,10 @@ class PersEventEmitter {
|
|
|
10088
10589
|
id: generateEventId(),
|
|
10089
10590
|
timestamp: Date.now()
|
|
10090
10591
|
};
|
|
10592
|
+
// Clear cached auth on auth failure (session invalid)
|
|
10593
|
+
if (event.domain === 'authentication' && event.type === 'session_restoration_failed') {
|
|
10594
|
+
this.lastAuthEvent = null;
|
|
10595
|
+
}
|
|
10091
10596
|
this.notifyHandlers(fullEvent);
|
|
10092
10597
|
return fullEvent;
|
|
10093
10598
|
}
|
|
@@ -10211,7 +10716,8 @@ class AuthManager {
|
|
|
10211
10716
|
this.events?.emitSuccess({
|
|
10212
10717
|
domain: 'authentication',
|
|
10213
10718
|
type: 'login_success',
|
|
10214
|
-
userMessage: 'Successfully logged in'
|
|
10719
|
+
userMessage: 'Successfully logged in',
|
|
10720
|
+
details: { user: result.user }
|
|
10215
10721
|
});
|
|
10216
10722
|
return result;
|
|
10217
10723
|
}
|
|
@@ -10259,7 +10765,66 @@ class AuthManager {
|
|
|
10259
10765
|
*/
|
|
10260
10766
|
async loginAsBusiness(jwtToken, options) {
|
|
10261
10767
|
const authService = this.apiClient.getAuthService();
|
|
10262
|
-
|
|
10768
|
+
const result = await authService.loginBusiness(jwtToken, options);
|
|
10769
|
+
this.events?.emitSuccess({
|
|
10770
|
+
domain: 'authentication',
|
|
10771
|
+
type: 'login_success',
|
|
10772
|
+
userMessage: 'Successfully logged in as business',
|
|
10773
|
+
details: { user: result.user, business: result.business }
|
|
10774
|
+
});
|
|
10775
|
+
return result;
|
|
10776
|
+
}
|
|
10777
|
+
/**
|
|
10778
|
+
* Login as tenant admin with JWT token
|
|
10779
|
+
*
|
|
10780
|
+
* Authenticates an admin in a tenant context.
|
|
10781
|
+
*
|
|
10782
|
+
* **Auto-Selection Behavior:**
|
|
10783
|
+
* - If admin has access to a single tenant, it's auto-selected
|
|
10784
|
+
* - If admin has access to multiple tenants and no tenantId is provided,
|
|
10785
|
+
* throws `MULTIPLE_CONTEXT_SELECTION_REQUIRED` error with available options
|
|
10786
|
+
*
|
|
10787
|
+
* @param jwtToken - JWT token from auth provider (Firebase, etc.)
|
|
10788
|
+
* @param options - Tenant authentication options (tenantId for multi-tenant admins)
|
|
10789
|
+
* @returns Promise resolving to authentication response with tenant context
|
|
10790
|
+
* @throws Error with code `MULTIPLE_CONTEXT_SELECTION_REQUIRED` when tenantId is needed
|
|
10791
|
+
*
|
|
10792
|
+
* @example Single Tenant Access
|
|
10793
|
+
* ```typescript
|
|
10794
|
+
* // Auto-selects the admin's only tenant
|
|
10795
|
+
* const result = await sdk.auth.loginAsTenant(jwt);
|
|
10796
|
+
* console.log('Tenant:', result.admin?.tenantId);
|
|
10797
|
+
* ```
|
|
10798
|
+
*
|
|
10799
|
+
* @example Multiple Tenant Access
|
|
10800
|
+
* ```typescript
|
|
10801
|
+
* try {
|
|
10802
|
+
* const result = await sdk.auth.loginAsTenant(jwt);
|
|
10803
|
+
* } catch (error) {
|
|
10804
|
+
* if (error.code === 'MULTIPLE_CONTEXT_SELECTION_REQUIRED') {
|
|
10805
|
+
* // Show tenant selector UI
|
|
10806
|
+
* const selectedId = await showTenantSelector(error.details.availableOptions);
|
|
10807
|
+
* const result = await sdk.auth.loginAsTenant(jwt, { tenantId: selectedId });
|
|
10808
|
+
* }
|
|
10809
|
+
* }
|
|
10810
|
+
* ```
|
|
10811
|
+
*
|
|
10812
|
+
* @example Direct Tenant Selection
|
|
10813
|
+
* ```typescript
|
|
10814
|
+
* const result = await sdk.auth.loginAsTenant(jwt, { tenantId: 'tenant-123' });
|
|
10815
|
+
* console.log('Authenticated as admin for tenant:', result.admin?.tenantId);
|
|
10816
|
+
* ```
|
|
10817
|
+
*/
|
|
10818
|
+
async loginAsTenant(jwtToken, options) {
|
|
10819
|
+
const authService = this.apiClient.getAuthService();
|
|
10820
|
+
const result = await authService.loginTenantAdmin(jwtToken, options);
|
|
10821
|
+
this.events?.emitSuccess({
|
|
10822
|
+
domain: 'authentication',
|
|
10823
|
+
type: 'login_success',
|
|
10824
|
+
userMessage: 'Successfully logged in as tenant admin',
|
|
10825
|
+
details: { admin: result.admin }
|
|
10826
|
+
});
|
|
10827
|
+
return result;
|
|
10263
10828
|
}
|
|
10264
10829
|
/**
|
|
10265
10830
|
* Get current business context
|
|
@@ -10277,7 +10842,27 @@ class AuthManager {
|
|
|
10277
10842
|
* ```
|
|
10278
10843
|
*/
|
|
10279
10844
|
async getCurrentBusiness() {
|
|
10280
|
-
|
|
10845
|
+
const authService = this.apiClient.getAuthService();
|
|
10846
|
+
return authService.getCurrentBusiness();
|
|
10847
|
+
}
|
|
10848
|
+
/**
|
|
10849
|
+
* Get current admin context
|
|
10850
|
+
*
|
|
10851
|
+
* Retrieves the current admin data if authenticated as tenant admin.
|
|
10852
|
+
* Requires prior tenant authentication via {@link loginAsTenant}.
|
|
10853
|
+
*
|
|
10854
|
+
* @returns Promise resolving to current admin data
|
|
10855
|
+
* @throws {PersApiError} When not authenticated as tenant admin
|
|
10856
|
+
*
|
|
10857
|
+
* @example
|
|
10858
|
+
* ```typescript
|
|
10859
|
+
* const admin = await sdk.auth.getCurrentAdmin();
|
|
10860
|
+
* console.log('Current admin:', admin.email);
|
|
10861
|
+
* ```
|
|
10862
|
+
*/
|
|
10863
|
+
async getCurrentAdmin() {
|
|
10864
|
+
const authService = this.apiClient.getAuthService();
|
|
10865
|
+
return authService.getCurrentAdmin();
|
|
10281
10866
|
}
|
|
10282
10867
|
/**
|
|
10283
10868
|
* Login with raw user data
|
|
@@ -10321,7 +10906,8 @@ class AuthManager {
|
|
|
10321
10906
|
* ```
|
|
10322
10907
|
*/
|
|
10323
10908
|
async getCurrentUser() {
|
|
10324
|
-
|
|
10909
|
+
const authService = this.apiClient.getAuthService();
|
|
10910
|
+
return authService.getCurrentUser();
|
|
10325
10911
|
}
|
|
10326
10912
|
/**
|
|
10327
10913
|
* Check if user is authenticated
|
|
@@ -10399,6 +10985,11 @@ class AuthManager {
|
|
|
10399
10985
|
async clearAuth() {
|
|
10400
10986
|
const authService = this.apiClient.getAuthService();
|
|
10401
10987
|
await authService.clearTokens();
|
|
10988
|
+
this.events?.emitSuccess({
|
|
10989
|
+
domain: 'authentication',
|
|
10990
|
+
type: 'logout_success',
|
|
10991
|
+
userMessage: 'Successfully logged out'
|
|
10992
|
+
});
|
|
10402
10993
|
}
|
|
10403
10994
|
/**
|
|
10404
10995
|
* Ensure authentication token is valid
|
|
@@ -12123,7 +12714,7 @@ class CampaignManager {
|
|
|
12123
12714
|
* Returns campaigns with pagination metadata for efficient data loading.
|
|
12124
12715
|
* Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all.
|
|
12125
12716
|
*
|
|
12126
|
-
* @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, tag, search, businessId, include)
|
|
12717
|
+
* @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, tag, search, businessId, startDate, endDate, include)
|
|
12127
12718
|
* @returns Promise resolving to paginated campaigns with metadata
|
|
12128
12719
|
*
|
|
12129
12720
|
* @example
|
|
@@ -12153,18 +12744,22 @@ class CampaignManager {
|
|
|
12153
12744
|
* active: true
|
|
12154
12745
|
* });
|
|
12155
12746
|
*
|
|
12156
|
-
* //
|
|
12157
|
-
* const
|
|
12158
|
-
*
|
|
12159
|
-
*
|
|
12160
|
-
*
|
|
12161
|
-
*
|
|
12162
|
-
*
|
|
12747
|
+
* // Filter by date range
|
|
12748
|
+
* const dateFiltered = await sdk.campaigns.getCampaigns({
|
|
12749
|
+
* startDate: new Date('2026-05-01'),
|
|
12750
|
+
* endDate: new Date('2026-12-31'),
|
|
12751
|
+
* active: true
|
|
12752
|
+
* });
|
|
12753
|
+
*
|
|
12754
|
+
* // Get campaigns with claim count
|
|
12755
|
+
* const withCounts = await sdk.campaigns.getCampaigns({
|
|
12756
|
+
* include: ['claimCount']
|
|
12163
12757
|
* });
|
|
12758
|
+
* withCounts.data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
|
|
12164
12759
|
*
|
|
12165
|
-
* // Include related data (trigger sources and
|
|
12760
|
+
* // Include related data (trigger sources, businesses, and claim count)
|
|
12166
12761
|
* const campaignsWithRelations = await sdk.campaigns.getCampaigns({
|
|
12167
|
-
* include: ['triggerSources', 'businesses']
|
|
12762
|
+
* include: ['triggerSources', 'businesses', 'claimCount']
|
|
12168
12763
|
* });
|
|
12169
12764
|
* ```
|
|
12170
12765
|
*/
|
|
@@ -12615,58 +13210,52 @@ class CampaignManager {
|
|
|
12615
13210
|
async deleteCampaignBusinessEngagement(campaignId, engagementId) {
|
|
12616
13211
|
return this.campaignService.deleteCampaignBusinessEngagement(campaignId, engagementId);
|
|
12617
13212
|
}
|
|
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
13213
|
/**
|
|
12650
13214
|
* Admin: Get campaign claims with optional filters
|
|
12651
13215
|
*
|
|
12652
|
-
* Retrieves campaign claims with optional filtering by campaign, user, or
|
|
13216
|
+
* Retrieves campaign claims with optional filtering by campaign, user, business, or date range.
|
|
12653
13217
|
* This operation requires administrator privileges and provides system-wide
|
|
12654
13218
|
* visibility into campaign performance and user engagement.
|
|
12655
13219
|
*
|
|
12656
13220
|
* @param filters - Optional filters and pagination options
|
|
13221
|
+
* @param filters.campaignId - Filter by specific campaign ID
|
|
13222
|
+
* @param filters.userId - Filter by specific user ID
|
|
13223
|
+
* @param filters.businessId - Filter by specific business ID
|
|
13224
|
+
* @param filters.dateFrom - Filter claims from this date (inclusive)
|
|
13225
|
+
* @param filters.dateTo - Filter claims until this date (inclusive)
|
|
13226
|
+
* @param filters.page - Page number for pagination
|
|
13227
|
+
* @param filters.limit - Items per page
|
|
13228
|
+
* @param filters.sortBy - Field to sort by
|
|
13229
|
+
* @param filters.sortOrder - Sort direction ('asc' or 'desc')
|
|
13230
|
+
* @param include - Optional relations to include for enrichment
|
|
12657
13231
|
* @returns Promise resolving to paginated campaign claims
|
|
12658
13232
|
* @throws {PersApiError} When not authenticated as administrator
|
|
12659
13233
|
*
|
|
12660
|
-
* @example
|
|
13234
|
+
* @example Get All Claims
|
|
12661
13235
|
* ```typescript
|
|
12662
|
-
*
|
|
12663
|
-
*
|
|
13236
|
+
* const { data: allClaims, pagination } = await sdk.campaigns.getCampaignClaims();
|
|
13237
|
+
* console.log(`Total: ${pagination.total}`);
|
|
13238
|
+
* ```
|
|
12664
13239
|
*
|
|
12665
|
-
*
|
|
12666
|
-
*
|
|
13240
|
+
* @example Filter by User
|
|
13241
|
+
* ```typescript
|
|
13242
|
+
* const { data: userClaims } = await sdk.campaigns.getCampaignClaims({ userId: 'user-123' });
|
|
13243
|
+
* ```
|
|
12667
13244
|
*
|
|
12668
|
-
*
|
|
12669
|
-
*
|
|
13245
|
+
* @example Filter by Date Range
|
|
13246
|
+
* ```typescript
|
|
13247
|
+
* // Get claims within a specific date range
|
|
13248
|
+
* const { data: monthClaims } = await sdk.campaigns.getCampaignClaims({
|
|
13249
|
+
* dateFrom: new Date('2024-01-01'),
|
|
13250
|
+
* dateTo: new Date('2024-01-31'),
|
|
13251
|
+
* limit: 100
|
|
13252
|
+
* });
|
|
13253
|
+
* console.log(`${monthClaims.length} claims in January 2024`);
|
|
13254
|
+
* ```
|
|
13255
|
+
*
|
|
13256
|
+
* @example With Pagination and Campaign Filter
|
|
13257
|
+
* ```typescript
|
|
13258
|
+
* const { data: page1 } = await sdk.campaigns.getCampaignClaims({
|
|
12670
13259
|
* campaignId: 'campaign-456',
|
|
12671
13260
|
* page: 1,
|
|
12672
13261
|
* limit: 50
|
|
@@ -13271,9 +13860,19 @@ class RedemptionManager {
|
|
|
13271
13860
|
*
|
|
13272
13861
|
* Retrieves all redemption redeems across the platform with filtering capabilities.
|
|
13273
13862
|
* 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)
|
|
13863
|
+
* activity. Supports filtering by user, redemption offer, date range, and enrichment of related entities.
|
|
13864
|
+
*
|
|
13865
|
+
* @param filters - Filter options (userId, redemptionId, businessId, status, dateFrom, dateTo, pagination)
|
|
13866
|
+
* @param filters.userId - Filter by specific user ID
|
|
13867
|
+
* @param filters.redemptionId - Filter by specific redemption offer ID
|
|
13868
|
+
* @param filters.businessId - Filter by specific business ID
|
|
13869
|
+
* @param filters.status - Filter by redeem status
|
|
13870
|
+
* @param filters.dateFrom - Filter redeems from this date (inclusive)
|
|
13871
|
+
* @param filters.dateTo - Filter redeems until this date (inclusive)
|
|
13872
|
+
* @param filters.page - Page number for pagination
|
|
13873
|
+
* @param filters.limit - Items per page
|
|
13874
|
+
* @param filters.sortBy - Field to sort by
|
|
13875
|
+
* @param filters.sortOrder - Sort direction ('asc' or 'desc')
|
|
13277
13876
|
* @param include - Optional relations to include for enrichment
|
|
13278
13877
|
* @returns Promise resolving to paginated list of redemption redeems
|
|
13279
13878
|
* @throws {PersApiError} When not authenticated as admin
|
|
@@ -13298,6 +13897,18 @@ class RedemptionManager {
|
|
|
13298
13897
|
* console.log(`User has ${userRedeems.length} redemptions`);
|
|
13299
13898
|
* ```
|
|
13300
13899
|
*
|
|
13900
|
+
* @example Filter by Date Range
|
|
13901
|
+
* ```typescript
|
|
13902
|
+
* // Get redeems within a specific date range
|
|
13903
|
+
* const { data: recentRedeems } = await sdk.redemptions.getRedemptionRedeems({
|
|
13904
|
+
* dateFrom: new Date('2024-01-01'),
|
|
13905
|
+
* dateTo: new Date('2024-01-31'),
|
|
13906
|
+
* limit: 100
|
|
13907
|
+
* });
|
|
13908
|
+
*
|
|
13909
|
+
* console.log(`${recentRedeems.length} redeems in January 2024`);
|
|
13910
|
+
* ```
|
|
13911
|
+
*
|
|
13301
13912
|
* @example With Enriched Data
|
|
13302
13913
|
* ```typescript
|
|
13303
13914
|
* const { data: redeems } = await sdk.redemptions.getRedemptionRedeems(
|
|
@@ -17026,6 +17637,12 @@ class PersEventsClient {
|
|
|
17026
17637
|
}
|
|
17027
17638
|
}
|
|
17028
17639
|
}
|
|
17640
|
+
/**
|
|
17641
|
+
* Create a PERS Events client instance
|
|
17642
|
+
*/
|
|
17643
|
+
function createPersEventsClient(config) {
|
|
17644
|
+
return new PersEventsClient(config);
|
|
17645
|
+
}
|
|
17029
17646
|
|
|
17030
17647
|
/**
|
|
17031
17648
|
* Wallet Events Manager - Real-time blockchain events for user's wallets
|
|
@@ -17228,7 +17845,8 @@ class WalletEventsManager {
|
|
|
17228
17845
|
}
|
|
17229
17846
|
// Save subscriptions for auto-resubscribe on reconnect
|
|
17230
17847
|
this.lastSubscriptions.wallets = wallets;
|
|
17231
|
-
|
|
17848
|
+
const result = await this.client.subscribeWallets(wallets);
|
|
17849
|
+
return result;
|
|
17232
17850
|
}
|
|
17233
17851
|
/**
|
|
17234
17852
|
* Subscribe to chain events (Admin Dashboard)
|
|
@@ -17251,7 +17869,8 @@ class WalletEventsManager {
|
|
|
17251
17869
|
}
|
|
17252
17870
|
// Save subscriptions for auto-resubscribe on reconnect
|
|
17253
17871
|
this.lastSubscriptions.chains = chains;
|
|
17254
|
-
|
|
17872
|
+
const result = await this.client.subscribeChains(chains);
|
|
17873
|
+
return result;
|
|
17255
17874
|
}
|
|
17256
17875
|
/**
|
|
17257
17876
|
* Unsubscribe from wallet events
|
|
@@ -17561,6 +18180,24 @@ class PersSDK {
|
|
|
17561
18180
|
this.apiClient.setEvents(this._events);
|
|
17562
18181
|
// Auto-connect to wallet events on successful login (if enabled)
|
|
17563
18182
|
this.setupWalletEventsAutoConnect();
|
|
18183
|
+
// Auto-restore session from stored tokens (if enabled, default: true)
|
|
18184
|
+
this.setupAutoRestoreSession();
|
|
18185
|
+
}
|
|
18186
|
+
/**
|
|
18187
|
+
* Setup automatic session restoration from stored tokens
|
|
18188
|
+
* @internal
|
|
18189
|
+
*/
|
|
18190
|
+
setupAutoRestoreSession() {
|
|
18191
|
+
const config = this.apiClient.getConfig();
|
|
18192
|
+
// Check if autoRestoreSession is enabled (default: true)
|
|
18193
|
+
if (config.autoRestoreSession === false) {
|
|
18194
|
+
return;
|
|
18195
|
+
}
|
|
18196
|
+
// Fire-and-forget: restore session asynchronously
|
|
18197
|
+
// Events will be emitted on success/failure
|
|
18198
|
+
this.restoreSession().catch(() => {
|
|
18199
|
+
// Error already emitted via session_restoration_failed event
|
|
18200
|
+
});
|
|
17564
18201
|
}
|
|
17565
18202
|
/**
|
|
17566
18203
|
* Setup auto-connect for wallet events on authentication
|
|
@@ -17638,22 +18275,23 @@ class PersSDK {
|
|
|
17638
18275
|
/**
|
|
17639
18276
|
* Restore user session from stored tokens
|
|
17640
18277
|
*
|
|
17641
|
-
*
|
|
17642
|
-
*
|
|
17643
|
-
*
|
|
18278
|
+
* **Note:** This method is called automatically on SDK initialization when
|
|
18279
|
+
* `autoRestoreSession` is enabled (default: true). You only need to call this
|
|
18280
|
+
* manually if you disabled auto-restore or need to force a session refresh.
|
|
18281
|
+
*
|
|
18282
|
+
* Validates stored tokens and fetches user data to restore the session.
|
|
18283
|
+
* Emits `session_restored` event on success, `session_restoration_failed` on error.
|
|
17644
18284
|
*
|
|
17645
18285
|
* **Important:** Only works for USER and BUSINESS accounts. Admin/tenant
|
|
17646
18286
|
* accounts don't support user data fetching, so this will return null for them
|
|
17647
18287
|
* (though their tokens remain valid for API calls).
|
|
17648
18288
|
*
|
|
17649
|
-
* Emits auth domain success event when session is restored.
|
|
17650
|
-
*
|
|
17651
18289
|
* @returns Promise resolving to User data if session restored, null if no session or admin account
|
|
17652
18290
|
* @throws {PersError} If token validation or user fetch fails
|
|
17653
18291
|
*
|
|
17654
18292
|
* @example
|
|
17655
18293
|
* ```typescript
|
|
17656
|
-
* //
|
|
18294
|
+
* // Manual restore (only needed if autoRestoreSession: false)
|
|
17657
18295
|
* const user = await sdk.restoreSession();
|
|
17658
18296
|
* if (user) {
|
|
17659
18297
|
* console.log('Welcome back,', user.name);
|
|
@@ -17667,25 +18305,57 @@ class PersSDK {
|
|
|
17667
18305
|
}
|
|
17668
18306
|
// Check auth type - only restore session for user and business accounts
|
|
17669
18307
|
const authProvider = this.apiClient.getConfig().authProvider;
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
18308
|
+
const authType = authProvider?.getAuthType ? await authProvider.getAuthType() : exports.AccountOwnerType.USER;
|
|
18309
|
+
if (authType === exports.AccountOwnerType.TENANT) {
|
|
18310
|
+
// Ensure token is valid (refresh if expired) before fetching admin
|
|
18311
|
+
try {
|
|
18312
|
+
await this.auth.ensureValidToken();
|
|
18313
|
+
// Fetch current admin data
|
|
18314
|
+
const adminData = await this.auth.getCurrentAdmin();
|
|
18315
|
+
// Emit session restored for tenant admin
|
|
18316
|
+
this._events.emitSuccess({
|
|
18317
|
+
type: 'session_restored',
|
|
18318
|
+
domain: 'authentication',
|
|
18319
|
+
userMessage: 'Admin session restored successfully',
|
|
18320
|
+
details: { admin: adminData }
|
|
18321
|
+
});
|
|
17675
18322
|
return null;
|
|
17676
18323
|
}
|
|
18324
|
+
catch (error) {
|
|
18325
|
+
await this.auth.clearAuth();
|
|
18326
|
+
this._events.emitError({
|
|
18327
|
+
type: 'session_restoration_failed',
|
|
18328
|
+
domain: 'authentication',
|
|
18329
|
+
userMessage: 'Admin session restoration failed',
|
|
18330
|
+
code: 'SESSION_INVALID',
|
|
18331
|
+
details: { error }
|
|
18332
|
+
});
|
|
18333
|
+
throw error;
|
|
18334
|
+
}
|
|
17677
18335
|
}
|
|
17678
18336
|
try {
|
|
17679
18337
|
// Ensure token is valid (refresh if expired) before fetching user
|
|
17680
18338
|
await this.auth.ensureValidToken();
|
|
17681
|
-
//
|
|
18339
|
+
// For BUSINESS auth, only fetch business context (no access to /users/me)
|
|
18340
|
+
if (authType === exports.AccountOwnerType.BUSINESS) {
|
|
18341
|
+
const businessData = await this.auth.getCurrentBusiness();
|
|
18342
|
+
// Emit event with business data (no user data for business auth)
|
|
18343
|
+
this._events.emitSuccess({
|
|
18344
|
+
type: 'session_restored',
|
|
18345
|
+
domain: 'authentication',
|
|
18346
|
+
userMessage: 'Business session restored successfully',
|
|
18347
|
+
details: { business: businessData }
|
|
18348
|
+
});
|
|
18349
|
+
return null; // No user data for business auth
|
|
18350
|
+
}
|
|
18351
|
+
// For USER auth, fetch user data
|
|
17682
18352
|
const userData = await this.auth.getCurrentUser();
|
|
17683
|
-
// Emit event
|
|
18353
|
+
// Emit event with user data
|
|
17684
18354
|
this._events.emitSuccess({
|
|
17685
18355
|
type: 'session_restored',
|
|
17686
18356
|
domain: 'authentication',
|
|
17687
18357
|
userMessage: 'Session restored successfully',
|
|
17688
|
-
details: {
|
|
18358
|
+
details: { user: userData }
|
|
17689
18359
|
});
|
|
17690
18360
|
return userData;
|
|
17691
18361
|
}
|
|
@@ -18178,6 +18848,114 @@ class PersSDK {
|
|
|
18178
18848
|
return !!this.apiClient.getConfig().authProvider;
|
|
18179
18849
|
}
|
|
18180
18850
|
}
|
|
18851
|
+
/**
|
|
18852
|
+
* Factory function for creating PERS SDK
|
|
18853
|
+
*
|
|
18854
|
+
* @param httpClient Platform-specific HTTP client implementation
|
|
18855
|
+
* @param config SDK configuration options
|
|
18856
|
+
* @returns PERS SDK instance
|
|
18857
|
+
*/
|
|
18858
|
+
function createPersSDK(httpClient, config) {
|
|
18859
|
+
return new PersSDK(httpClient, config);
|
|
18860
|
+
}
|
|
18861
|
+
|
|
18862
|
+
class IndexedDBTokenStorage {
|
|
18863
|
+
constructor() {
|
|
18864
|
+
this.supportsObjects = true;
|
|
18865
|
+
this.dbPromise = null;
|
|
18866
|
+
if (typeof indexedDB === 'undefined') {
|
|
18867
|
+
console.warn('IndexedDB is not available in this environment');
|
|
18868
|
+
}
|
|
18869
|
+
}
|
|
18870
|
+
getDB() {
|
|
18871
|
+
if (this.dbPromise)
|
|
18872
|
+
return this.dbPromise;
|
|
18873
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
18874
|
+
if (typeof indexedDB === 'undefined') {
|
|
18875
|
+
return reject(new Error('IndexedDB not supported'));
|
|
18876
|
+
}
|
|
18877
|
+
const request = indexedDB.open(IndexedDBTokenStorage.DB_NAME, IndexedDBTokenStorage.DB_VERSION);
|
|
18878
|
+
request.onupgradeneeded = (event) => {
|
|
18879
|
+
const db = event.target.result;
|
|
18880
|
+
if (!db.objectStoreNames.contains(IndexedDBTokenStorage.STORE_NAME)) {
|
|
18881
|
+
db.createObjectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18882
|
+
}
|
|
18883
|
+
};
|
|
18884
|
+
request.onsuccess = (event) => {
|
|
18885
|
+
resolve(event.target.result);
|
|
18886
|
+
};
|
|
18887
|
+
request.onerror = (event) => {
|
|
18888
|
+
reject(event.target.error);
|
|
18889
|
+
};
|
|
18890
|
+
});
|
|
18891
|
+
return this.dbPromise;
|
|
18892
|
+
}
|
|
18893
|
+
async get(key) {
|
|
18894
|
+
try {
|
|
18895
|
+
const db = await this.getDB();
|
|
18896
|
+
return new Promise((resolve, reject) => {
|
|
18897
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readonly');
|
|
18898
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18899
|
+
const request = store.get(key);
|
|
18900
|
+
request.onsuccess = () => resolve(request.result || null);
|
|
18901
|
+
request.onerror = () => reject(request.error);
|
|
18902
|
+
});
|
|
18903
|
+
}
|
|
18904
|
+
catch (e) {
|
|
18905
|
+
console.warn('[IndexedDBTokenStorage] Failed to get key', key, e);
|
|
18906
|
+
return null;
|
|
18907
|
+
}
|
|
18908
|
+
}
|
|
18909
|
+
async set(key, value) {
|
|
18910
|
+
try {
|
|
18911
|
+
const db = await this.getDB();
|
|
18912
|
+
return new Promise((resolve, reject) => {
|
|
18913
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
|
|
18914
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18915
|
+
const request = store.put(value, key);
|
|
18916
|
+
request.onsuccess = () => resolve();
|
|
18917
|
+
request.onerror = () => reject(request.error);
|
|
18918
|
+
});
|
|
18919
|
+
}
|
|
18920
|
+
catch (e) {
|
|
18921
|
+
console.error('[IndexedDBTokenStorage] Failed to set key', key, e);
|
|
18922
|
+
throw e;
|
|
18923
|
+
}
|
|
18924
|
+
}
|
|
18925
|
+
async remove(key) {
|
|
18926
|
+
try {
|
|
18927
|
+
const db = await this.getDB();
|
|
18928
|
+
return new Promise((resolve, reject) => {
|
|
18929
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
|
|
18930
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18931
|
+
const request = store.delete(key);
|
|
18932
|
+
request.onsuccess = () => resolve();
|
|
18933
|
+
request.onerror = () => reject(request.error);
|
|
18934
|
+
});
|
|
18935
|
+
}
|
|
18936
|
+
catch (e) {
|
|
18937
|
+
console.warn('[IndexedDBTokenStorage] Failed to remove key', key, e);
|
|
18938
|
+
}
|
|
18939
|
+
}
|
|
18940
|
+
async clear() {
|
|
18941
|
+
try {
|
|
18942
|
+
const db = await this.getDB();
|
|
18943
|
+
return new Promise((resolve, reject) => {
|
|
18944
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
|
|
18945
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18946
|
+
const request = store.clear();
|
|
18947
|
+
request.onsuccess = () => resolve();
|
|
18948
|
+
request.onerror = () => reject(request.error);
|
|
18949
|
+
});
|
|
18950
|
+
}
|
|
18951
|
+
catch (e) {
|
|
18952
|
+
console.warn('[IndexedDBTokenStorage] Failed to clear storage', e);
|
|
18953
|
+
}
|
|
18954
|
+
}
|
|
18955
|
+
}
|
|
18956
|
+
IndexedDBTokenStorage.DB_NAME = 'pers-sdk-storage';
|
|
18957
|
+
IndexedDBTokenStorage.STORE_NAME = 'auth-tokens';
|
|
18958
|
+
IndexedDBTokenStorage.DB_VERSION = 1;
|
|
18181
18959
|
|
|
18182
18960
|
/**
|
|
18183
18961
|
* Environment Detection Utility
|
|
@@ -18208,7 +18986,16 @@ function detectEnvironment() {
|
|
|
18208
18986
|
/**
|
|
18209
18987
|
* Global environment info (cached)
|
|
18210
18988
|
*/
|
|
18211
|
-
detectEnvironment();
|
|
18989
|
+
const environment = detectEnvironment();
|
|
18990
|
+
/**
|
|
18991
|
+
* Warns if environment might have bundling issues
|
|
18992
|
+
*/
|
|
18993
|
+
function warnIfProblematicEnvironment(feature) {
|
|
18994
|
+
if (environment.needsNodeStreamPolyfills) {
|
|
18995
|
+
console.warn(`[PERS SDK] ${feature} may require additional polyfills in web environments. ` +
|
|
18996
|
+
`If you encounter 'readable-stream' errors, ensure proper polyfills are configured.`);
|
|
18997
|
+
}
|
|
18998
|
+
}
|
|
18212
18999
|
|
|
18213
19000
|
/**
|
|
18214
19001
|
* AsyncStorage Token Storage for React Native Mobile Platforms
|
|
@@ -18821,6 +19608,7 @@ const SDKContext = react.createContext(null);
|
|
|
18821
19608
|
*/
|
|
18822
19609
|
const PersSDKProvider = ({ children, config }) => {
|
|
18823
19610
|
const initializingRef = react.useRef(false);
|
|
19611
|
+
const eventUnsubscribeRef = react.useRef(null);
|
|
18824
19612
|
// State refs for stable functions to read current values
|
|
18825
19613
|
const sdkRef = react.useRef(null);
|
|
18826
19614
|
const isInitializedRef = react.useRef(false);
|
|
@@ -18874,6 +19662,21 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
18874
19662
|
};
|
|
18875
19663
|
// Initialize PersSDK with platform-aware auth provider
|
|
18876
19664
|
const sdkInstance = new PersSDK(httpClient, sdkConfig);
|
|
19665
|
+
// Subscribe to auth events BEFORE exposing SDK to consumers
|
|
19666
|
+
// This ensures no events are missed due to React timing
|
|
19667
|
+
eventUnsubscribeRef.current = sdkInstance.events.subscribe((event) => {
|
|
19668
|
+
if (event.type === 'session_restored' || event.type === 'login_success') {
|
|
19669
|
+
const details = event.details;
|
|
19670
|
+
if (details?.user) {
|
|
19671
|
+
setUser(details.user);
|
|
19672
|
+
setIsAuthenticated(true);
|
|
19673
|
+
}
|
|
19674
|
+
}
|
|
19675
|
+
if (event.type === 'logout_success' || event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
|
|
19676
|
+
setUser(null);
|
|
19677
|
+
setIsAuthenticated(false);
|
|
19678
|
+
}
|
|
19679
|
+
}, { domains: ['authentication'], replay: true });
|
|
18877
19680
|
setAuthProvider(authProvider);
|
|
18878
19681
|
setSdk(sdkInstance);
|
|
18879
19682
|
setIsInitialized(true);
|
|
@@ -18887,6 +19690,15 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
18887
19690
|
initializingRef.current = false;
|
|
18888
19691
|
}
|
|
18889
19692
|
}, [isInitialized]);
|
|
19693
|
+
// Cleanup subscription on unmount
|
|
19694
|
+
react.useEffect(() => {
|
|
19695
|
+
return () => {
|
|
19696
|
+
if (eventUnsubscribeRef.current) {
|
|
19697
|
+
eventUnsubscribeRef.current();
|
|
19698
|
+
eventUnsubscribeRef.current = null;
|
|
19699
|
+
}
|
|
19700
|
+
};
|
|
19701
|
+
}, []);
|
|
18890
19702
|
const setAuthenticationState = react.useCallback((user, isAuthenticated) => {
|
|
18891
19703
|
setUser(user);
|
|
18892
19704
|
setIsAuthenticated(isAuthenticated);
|
|
@@ -18933,42 +19745,7 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
18933
19745
|
console.error('[PersSDK] Failed to restore session:', error);
|
|
18934
19746
|
throw error;
|
|
18935
19747
|
}
|
|
18936
|
-
}, [setAuthenticationState]);
|
|
18937
|
-
// Listen for authentication events from core SDK
|
|
18938
|
-
// Set up immediately when SDK is created (don't wait for isInitialized)
|
|
18939
|
-
// to catch session_restored events that fire during SDK initialization
|
|
18940
|
-
react.useEffect(() => {
|
|
18941
|
-
if (!sdk)
|
|
18942
|
-
return;
|
|
18943
|
-
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
|
-
});
|
|
18960
|
-
}
|
|
18961
|
-
}
|
|
18962
|
-
// Session restoration failed or auth error - clear React state
|
|
18963
|
-
if (event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
|
|
18964
|
-
console.log('[PersSDK] Authentication failed - clearing session');
|
|
18965
|
-
setAuthenticationState(null, false);
|
|
18966
|
-
}
|
|
18967
|
-
}, { domains: ['authentication'] });
|
|
18968
|
-
return () => {
|
|
18969
|
-
unsubscribe();
|
|
18970
|
-
};
|
|
18971
|
-
}, [sdk, setAuthenticationState]);
|
|
19748
|
+
}, [setAuthenticationState]);
|
|
18972
19749
|
// iOS/Android: Monitor app state and validate tokens when app becomes active
|
|
18973
19750
|
react.useEffect(() => {
|
|
18974
19751
|
if (!sdk || reactNative.Platform.OS === 'web') {
|
|
@@ -18976,7 +19753,6 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
18976
19753
|
}
|
|
18977
19754
|
const handleAppStateChange = async (nextAppState) => {
|
|
18978
19755
|
if (nextAppState === 'active' && isAuthenticated) {
|
|
18979
|
-
console.log('[PersSDK] App became active - validating tokens');
|
|
18980
19756
|
try {
|
|
18981
19757
|
// Trigger token validation when app resumes from background
|
|
18982
19758
|
// This ensures tokens are checked after extended inactivity
|
|
@@ -19066,7 +19842,7 @@ const usePersSDK = () => {
|
|
|
19066
19842
|
* ```
|
|
19067
19843
|
*/
|
|
19068
19844
|
const useAuth = () => {
|
|
19069
|
-
const { sdk, authProvider, isInitialized, isAuthenticated, user,
|
|
19845
|
+
const { sdk, authProvider, isInitialized, isAuthenticated, user, refreshUserData } = usePersSDK();
|
|
19070
19846
|
/**
|
|
19071
19847
|
* Authenticates a user with a JWT token
|
|
19072
19848
|
*
|
|
@@ -19089,22 +19865,15 @@ const useAuth = () => {
|
|
|
19089
19865
|
try {
|
|
19090
19866
|
// Set token in auth provider
|
|
19091
19867
|
await authProvider.setAccessToken(jwtToken);
|
|
19092
|
-
// Perform login
|
|
19868
|
+
// Perform login - emits login_success event which Provider handles
|
|
19093
19869
|
const result = await sdk.auth.loginWithToken(jwtToken, userType);
|
|
19094
|
-
|
|
19095
|
-
if (userData) {
|
|
19096
|
-
setAuthenticationState(userData, true);
|
|
19097
|
-
return result;
|
|
19098
|
-
}
|
|
19099
|
-
else {
|
|
19100
|
-
throw new Error('No user data returned from login');
|
|
19101
|
-
}
|
|
19870
|
+
return result;
|
|
19102
19871
|
}
|
|
19103
19872
|
catch (error) {
|
|
19104
19873
|
console.error('Login failed:', error);
|
|
19105
19874
|
throw error;
|
|
19106
19875
|
}
|
|
19107
|
-
}, [sdk, authProvider
|
|
19876
|
+
}, [sdk, authProvider]);
|
|
19108
19877
|
/**
|
|
19109
19878
|
* Logs in a user using raw user data (direct authentication bypass)
|
|
19110
19879
|
*
|
|
@@ -19122,25 +19891,18 @@ const useAuth = () => {
|
|
|
19122
19891
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
19123
19892
|
}
|
|
19124
19893
|
try {
|
|
19125
|
-
// Use the raw data login
|
|
19894
|
+
// Use the raw data login - emits login_success event which Provider handles
|
|
19126
19895
|
const result = await sdk.auth.loginWithRawData(rawUserData);
|
|
19127
19896
|
// Set token from result
|
|
19128
19897
|
if (result.accessToken) {
|
|
19129
19898
|
await authProvider.setAccessToken(result.accessToken);
|
|
19130
19899
|
}
|
|
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
19900
|
}
|
|
19139
19901
|
catch (error) {
|
|
19140
19902
|
console.error('Raw data login failed:', error);
|
|
19141
19903
|
throw error;
|
|
19142
19904
|
}
|
|
19143
|
-
}, [sdk, authProvider
|
|
19905
|
+
}, [sdk, authProvider]);
|
|
19144
19906
|
/**
|
|
19145
19907
|
* Logs out the current user and clears authentication state
|
|
19146
19908
|
*
|
|
@@ -19154,17 +19916,18 @@ const useAuth = () => {
|
|
|
19154
19916
|
* ```
|
|
19155
19917
|
*/
|
|
19156
19918
|
const logout = react.useCallback(async () => {
|
|
19919
|
+
if (!sdk) {
|
|
19920
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
19921
|
+
}
|
|
19157
19922
|
try {
|
|
19158
|
-
|
|
19159
|
-
|
|
19160
|
-
}
|
|
19161
|
-
setAuthenticationState(null, false);
|
|
19923
|
+
// clearAuth emits logout_success event which Provider handles
|
|
19924
|
+
await sdk.auth.clearAuth();
|
|
19162
19925
|
}
|
|
19163
19926
|
catch (error) {
|
|
19164
19927
|
console.error('Logout failed:', error);
|
|
19165
19928
|
throw error;
|
|
19166
19929
|
}
|
|
19167
|
-
}, [
|
|
19930
|
+
}, [sdk]);
|
|
19168
19931
|
/**
|
|
19169
19932
|
* Retrieves the current authenticated user data
|
|
19170
19933
|
*
|
|
@@ -19238,9 +20001,9 @@ const useAuth = () => {
|
|
|
19238
20001
|
if (!sdk) {
|
|
19239
20002
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
19240
20003
|
}
|
|
20004
|
+
// clearAuth emits logout_success event which Provider handles
|
|
19241
20005
|
await sdk.auth.clearAuth();
|
|
19242
|
-
|
|
19243
|
-
}, [sdk, setAuthenticationState]);
|
|
20006
|
+
}, [sdk]);
|
|
19244
20007
|
/**
|
|
19245
20008
|
* Checks if the current authentication is valid (non-expired)
|
|
19246
20009
|
*
|
|
@@ -40275,6 +41038,581 @@ const useWeb3 = () => {
|
|
|
40275
41038
|
};
|
|
40276
41039
|
};
|
|
40277
41040
|
|
|
41041
|
+
/**
|
|
41042
|
+
* Token SDK - Class-based Promise SDK for Token Operations
|
|
41043
|
+
*
|
|
41044
|
+
* Modern, performant SDK with direct method access and excellent TypeScript support.
|
|
41045
|
+
* Optimized for bundle size, performance, and developer experience.
|
|
41046
|
+
*
|
|
41047
|
+
* Usage:
|
|
41048
|
+
* const tokenSDK = new TokenSDK(apiClient);
|
|
41049
|
+
* const tokens = await tokenSDK.getTokens();
|
|
41050
|
+
* const creditToken = await tokenSDK.getActiveCreditToken();
|
|
41051
|
+
*/
|
|
41052
|
+
class TokenSDK {
|
|
41053
|
+
constructor(apiClient) {
|
|
41054
|
+
this.tokenApi = new TokenApi(apiClient);
|
|
41055
|
+
this.tokenService = new TokenService(this.tokenApi);
|
|
41056
|
+
}
|
|
41057
|
+
// ==========================================
|
|
41058
|
+
// CONVENIENCE METHODS - Direct Promise API
|
|
41059
|
+
// ==========================================
|
|
41060
|
+
// Simplified method names for better developer experience
|
|
41061
|
+
/**
|
|
41062
|
+
* Get all tokens
|
|
41063
|
+
* @returns Promise with all available tokens
|
|
41064
|
+
*/
|
|
41065
|
+
async getTokens() {
|
|
41066
|
+
return this.tokenService.getRemoteTokens();
|
|
41067
|
+
}
|
|
41068
|
+
/**
|
|
41069
|
+
* Get all token types
|
|
41070
|
+
* @returns Promise with all available token types
|
|
41071
|
+
*/
|
|
41072
|
+
async getTokenTypes() {
|
|
41073
|
+
return this.tokenService.getRemoteTokenTypes();
|
|
41074
|
+
}
|
|
41075
|
+
/**
|
|
41076
|
+
* Get the active credit token
|
|
41077
|
+
* @returns Promise with the current active credit token
|
|
41078
|
+
*/
|
|
41079
|
+
async getActiveCreditToken() {
|
|
41080
|
+
return this.tokenService.getRemoteActiveCreditToken();
|
|
41081
|
+
}
|
|
41082
|
+
/**
|
|
41083
|
+
* Get all reward tokens
|
|
41084
|
+
* @returns Promise with all reward tokens
|
|
41085
|
+
*/
|
|
41086
|
+
async getRewardTokens() {
|
|
41087
|
+
return this.tokenService.getRemoteRewardTokens();
|
|
41088
|
+
}
|
|
41089
|
+
/**
|
|
41090
|
+
* Get all status tokens
|
|
41091
|
+
* @returns Promise with all status tokens
|
|
41092
|
+
*/
|
|
41093
|
+
async getStatusTokens() {
|
|
41094
|
+
return this.tokenService.getRemoteStatusTokens();
|
|
41095
|
+
}
|
|
41096
|
+
/**
|
|
41097
|
+
* Get token by contract address
|
|
41098
|
+
* @param contractAddress - The contract address to search for
|
|
41099
|
+
* @param contractTokenId - Optional contract token ID
|
|
41100
|
+
* @returns Promise with the matching token
|
|
41101
|
+
*/
|
|
41102
|
+
async getTokenByContract(contractAddress, contractTokenId = null) {
|
|
41103
|
+
return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
41104
|
+
}
|
|
41105
|
+
// ==========================================
|
|
41106
|
+
// ADMIN METHODS - Token Management
|
|
41107
|
+
// ==========================================
|
|
41108
|
+
/**
|
|
41109
|
+
* Create token metadata
|
|
41110
|
+
* @param tokenId - The token ID
|
|
41111
|
+
* @param tokenData - The token metadata request data
|
|
41112
|
+
* @returns Promise with the updated token
|
|
41113
|
+
*/
|
|
41114
|
+
async createTokenMetadata(tokenId, tokenData) {
|
|
41115
|
+
return this.tokenService.createTokenMetadata(tokenId, tokenData);
|
|
41116
|
+
}
|
|
41117
|
+
/**
|
|
41118
|
+
* Update token metadata (ERC721 only)
|
|
41119
|
+
* Note: Existing minted NFTs retain their original metadata - this only affects future mints
|
|
41120
|
+
* @param metadataId - The metadata ID to update
|
|
41121
|
+
* @param tokenData - The token metadata request data
|
|
41122
|
+
* @returns Promise with the updated metadata
|
|
41123
|
+
*/
|
|
41124
|
+
async updateTokenMetadata(metadataId, tokenData) {
|
|
41125
|
+
return this.tokenService.updateTokenMetadata(metadataId, tokenData);
|
|
41126
|
+
}
|
|
41127
|
+
/**
|
|
41128
|
+
* Toggle token active status
|
|
41129
|
+
* @param tokenId - The token ID to toggle
|
|
41130
|
+
* @returns Promise with the updated token
|
|
41131
|
+
*/
|
|
41132
|
+
async toggleTokenActive(tokenId) {
|
|
41133
|
+
return this.tokenService.toggleTokenActive(tokenId);
|
|
41134
|
+
}
|
|
41135
|
+
/**
|
|
41136
|
+
* Create a new token
|
|
41137
|
+
* @param tokenData - The token creation data
|
|
41138
|
+
* @returns Promise with the created token
|
|
41139
|
+
*/
|
|
41140
|
+
async createToken(tokenData) {
|
|
41141
|
+
return this.tokenService.createToken(tokenData);
|
|
41142
|
+
}
|
|
41143
|
+
/**
|
|
41144
|
+
* Update an existing token
|
|
41145
|
+
* @param tokenId - The token ID to update
|
|
41146
|
+
* @param tokenData - The token update data
|
|
41147
|
+
* @returns Promise with the updated token
|
|
41148
|
+
*/
|
|
41149
|
+
async updateToken(tokenId, tokenData) {
|
|
41150
|
+
return this.tokenService.updateToken(tokenId, tokenData);
|
|
41151
|
+
}
|
|
41152
|
+
/**
|
|
41153
|
+
* Set mainnet contract address for a token
|
|
41154
|
+
* @param tokenId - The token ID
|
|
41155
|
+
* @param contractAddress - The contract address
|
|
41156
|
+
* @param chainId - The blockchain chain ID
|
|
41157
|
+
* @returns Promise with the updated token
|
|
41158
|
+
*/
|
|
41159
|
+
async setMainnetContract(tokenId, contractAddress, chainId) {
|
|
41160
|
+
return this.tokenService.setMainnetContract(tokenId, contractAddress, chainId);
|
|
41161
|
+
}
|
|
41162
|
+
/**
|
|
41163
|
+
* Toggle token metadata status
|
|
41164
|
+
* @param metadataId - The metadata ID to toggle
|
|
41165
|
+
* @returns Promise with the updated metadata
|
|
41166
|
+
*/
|
|
41167
|
+
async toggleTokenMetadataStatus(metadataId) {
|
|
41168
|
+
return this.tokenService.toggleTokenMetadataStatus(metadataId);
|
|
41169
|
+
}
|
|
41170
|
+
/**
|
|
41171
|
+
* Create a new token type
|
|
41172
|
+
* @param tokenType - The token type data
|
|
41173
|
+
* @returns Promise with the created token type
|
|
41174
|
+
*/
|
|
41175
|
+
async createTokenType(tokenType) {
|
|
41176
|
+
return this.tokenService.createTokenType(tokenType);
|
|
41177
|
+
}
|
|
41178
|
+
// ==========================================
|
|
41179
|
+
// ADVANCED ACCESS - For Complex Operations
|
|
41180
|
+
// ==========================================
|
|
41181
|
+
/**
|
|
41182
|
+
* Get direct access to the token service for advanced operations
|
|
41183
|
+
* @returns The underlying TokenService instance
|
|
41184
|
+
*/
|
|
41185
|
+
getTokenService() {
|
|
41186
|
+
return this.tokenService;
|
|
41187
|
+
}
|
|
41188
|
+
/**
|
|
41189
|
+
* Get direct access to the token API for low-level operations
|
|
41190
|
+
* @returns The underlying TokenApi instance
|
|
41191
|
+
*/
|
|
41192
|
+
getTokenApi() {
|
|
41193
|
+
return this.tokenApi;
|
|
41194
|
+
}
|
|
41195
|
+
}
|
|
41196
|
+
|
|
41197
|
+
// ==========================================
|
|
41198
|
+
// TOKEN UTILITY FUNCTIONS
|
|
41199
|
+
// ==========================================
|
|
41200
|
+
/**
|
|
41201
|
+
* Get metadata from a token unit response
|
|
41202
|
+
* Handles metadata-based tokens (ERC-1155 with metadata array)
|
|
41203
|
+
*
|
|
41204
|
+
* For metadata-based tokens:
|
|
41205
|
+
* - Returns the metadata object from token.metadata array
|
|
41206
|
+
* - Uses provided incrementalId or tokenMetadataIncrementalId from tokenUnit
|
|
41207
|
+
* - Defaults to index 0 if no incrementalId specified
|
|
41208
|
+
*
|
|
41209
|
+
* For standard tokens (no metadata):
|
|
41210
|
+
* - Returns null
|
|
41211
|
+
*
|
|
41212
|
+
* @param tokenUnit - The token unit from campaign or balance
|
|
41213
|
+
* @param incrementalId - Optional metadata index to use (overrides tokenUnit.tokenMetadataIncrementalId)
|
|
41214
|
+
* @returns Token metadata object or null
|
|
41215
|
+
*
|
|
41216
|
+
* @example
|
|
41217
|
+
* ```typescript
|
|
41218
|
+
* // Get metadata using tokenUnit's incrementalId
|
|
41219
|
+
* const tokenUnit = {
|
|
41220
|
+
* tokenMetadataIncrementalId: 3,
|
|
41221
|
+
* token: {
|
|
41222
|
+
* metadata: [
|
|
41223
|
+
* { name: 'Bronze Pass', imageUrl: '...' },
|
|
41224
|
+
* { name: 'Silver Pass', imageUrl: '...' },
|
|
41225
|
+
* { name: 'Gold Pass', imageUrl: '...' }
|
|
41226
|
+
* ]
|
|
41227
|
+
* }
|
|
41228
|
+
* };
|
|
41229
|
+
* const metadata = getMetadataFromTokenUnitResponse(tokenUnit);
|
|
41230
|
+
* // Returns: { name: 'Bronze Pass', imageUrl: '...' } (index 0 - default)
|
|
41231
|
+
*
|
|
41232
|
+
* // Override incrementalId
|
|
41233
|
+
* const metadata2 = getMetadataFromTokenUnitResponse(tokenUnit, 2);
|
|
41234
|
+
* // Returns: { name: 'Gold Pass', imageUrl: '...' } (index 2)
|
|
41235
|
+
*
|
|
41236
|
+
* // Standard token (no metadata)
|
|
41237
|
+
* const tokenUnit2 = {
|
|
41238
|
+
* token: { symbol: 'VQP', name: 'Visit Qatar Points' }
|
|
41239
|
+
* };
|
|
41240
|
+
* const metadata3 = getMetadataFromTokenUnitResponse(tokenUnit2);
|
|
41241
|
+
* // Returns: null
|
|
41242
|
+
*
|
|
41243
|
+
* // Display name with fallback
|
|
41244
|
+
* const displayName = metadata?.name || tokenUnit.token?.symbol || 'Reward';
|
|
41245
|
+
*
|
|
41246
|
+
* // Display image
|
|
41247
|
+
* const imageUrl = metadata?.imageUrl;
|
|
41248
|
+
* ```
|
|
41249
|
+
*/
|
|
41250
|
+
function getMetadataFromTokenUnitResponse(tokenUnit, incrementalId) {
|
|
41251
|
+
// If no metadata array exists, return null
|
|
41252
|
+
if (!tokenUnit.token?.metadata || tokenUnit.token.metadata.length === 0) {
|
|
41253
|
+
return null;
|
|
41254
|
+
}
|
|
41255
|
+
const metadata = tokenUnit.token.metadata;
|
|
41256
|
+
// Use provided incrementalId or tokenUnit's incrementalId to find matching metadata
|
|
41257
|
+
const targetId = incrementalId ?? tokenUnit.tokenMetadataIncrementalId;
|
|
41258
|
+
if (targetId !== undefined) {
|
|
41259
|
+
const found = metadata.find((m) => m.incrementalId === targetId);
|
|
41260
|
+
if (found)
|
|
41261
|
+
return found;
|
|
41262
|
+
}
|
|
41263
|
+
// Fallback to first metadata item
|
|
41264
|
+
return metadata[0];
|
|
41265
|
+
}
|
|
41266
|
+
|
|
41267
|
+
/**
|
|
41268
|
+
* Abstract Base Token Service - Explicit Initialization Pattern
|
|
41269
|
+
*
|
|
41270
|
+
* Platform-agnostic token operations with Promise-based API.
|
|
41271
|
+
* Framework services extend this and control initialization lifecycle.
|
|
41272
|
+
*
|
|
41273
|
+
* Benefits:
|
|
41274
|
+
* - Explicit initialization control
|
|
41275
|
+
* - Better error boundaries
|
|
41276
|
+
* - Clear lifecycle management
|
|
41277
|
+
* - Testable initialization state
|
|
41278
|
+
* - Zero framework dependencies
|
|
41279
|
+
*/
|
|
41280
|
+
class BaseTokenService {
|
|
41281
|
+
constructor() {
|
|
41282
|
+
this._isInitialized = false;
|
|
41283
|
+
}
|
|
41284
|
+
// ==========================================
|
|
41285
|
+
// INITIALIZATION LIFECYCLE
|
|
41286
|
+
// ==========================================
|
|
41287
|
+
/**
|
|
41288
|
+
* LIFECYCLE: Initialize token service with API client
|
|
41289
|
+
* Must be called before using any token operations
|
|
41290
|
+
*/
|
|
41291
|
+
initializeTokenService(apiClient) {
|
|
41292
|
+
if (!apiClient) {
|
|
41293
|
+
throw new Error('Cannot initialize TokenService: apiClient is null or undefined');
|
|
41294
|
+
}
|
|
41295
|
+
if (!this._isInitialized) {
|
|
41296
|
+
this._tokenApi = new TokenApi(apiClient);
|
|
41297
|
+
this._tokenBusinessService = new TokenService(this._tokenApi);
|
|
41298
|
+
this._isInitialized = true;
|
|
41299
|
+
}
|
|
41300
|
+
}
|
|
41301
|
+
/**
|
|
41302
|
+
* LIFECYCLE: Check if token service is initialized
|
|
41303
|
+
*/
|
|
41304
|
+
get isTokenServiceInitialized() {
|
|
41305
|
+
return this._isInitialized;
|
|
41306
|
+
}
|
|
41307
|
+
/**
|
|
41308
|
+
* INTERNAL: Get token business service (throws if not initialized)
|
|
41309
|
+
*/
|
|
41310
|
+
get tokenBusinessService() {
|
|
41311
|
+
if (!this._tokenBusinessService) {
|
|
41312
|
+
throw new Error('TokenService not initialized. Call initializeTokenService(apiClient) first.');
|
|
41313
|
+
}
|
|
41314
|
+
return this._tokenBusinessService;
|
|
41315
|
+
}
|
|
41316
|
+
// ==========================================
|
|
41317
|
+
// PUBLIC OPERATIONS
|
|
41318
|
+
// ==========================================
|
|
41319
|
+
/**
|
|
41320
|
+
* PUBLIC: Get all remote tokens
|
|
41321
|
+
*/
|
|
41322
|
+
getRemoteTokens() {
|
|
41323
|
+
return this.tokenBusinessService.getRemoteTokens();
|
|
41324
|
+
}
|
|
41325
|
+
/**
|
|
41326
|
+
* PUBLIC: Get all remote token types
|
|
41327
|
+
*/
|
|
41328
|
+
getRemoteTokenTypes() {
|
|
41329
|
+
return this.tokenBusinessService.getRemoteTokenTypes();
|
|
41330
|
+
}
|
|
41331
|
+
/**
|
|
41332
|
+
* PUBLIC: Get active credit token
|
|
41333
|
+
*/
|
|
41334
|
+
getRemoteActiveCreditToken() {
|
|
41335
|
+
return this.tokenBusinessService.getRemoteActiveCreditToken();
|
|
41336
|
+
}
|
|
41337
|
+
/**
|
|
41338
|
+
* PUBLIC: Get reward tokens
|
|
41339
|
+
*/
|
|
41340
|
+
getRemoteRewardTokens() {
|
|
41341
|
+
return this.tokenBusinessService.getRemoteRewardTokens();
|
|
41342
|
+
}
|
|
41343
|
+
/**
|
|
41344
|
+
* PUBLIC: Get status tokens
|
|
41345
|
+
*/
|
|
41346
|
+
getRemoteStatusTokens() {
|
|
41347
|
+
return this.tokenBusinessService.getRemoteStatusTokens();
|
|
41348
|
+
}
|
|
41349
|
+
/**
|
|
41350
|
+
* PUBLIC: Get token by contract address
|
|
41351
|
+
*/
|
|
41352
|
+
getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
41353
|
+
return this.tokenBusinessService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
41354
|
+
}
|
|
41355
|
+
// ==========================================
|
|
41356
|
+
// ADMIN OPERATIONS
|
|
41357
|
+
// ==========================================
|
|
41358
|
+
/**
|
|
41359
|
+
* ADMIN: Create token metadata
|
|
41360
|
+
*/
|
|
41361
|
+
createTokenMetadata(tokenId, tokenData) {
|
|
41362
|
+
return this.tokenBusinessService.createTokenMetadata(tokenId, tokenData);
|
|
41363
|
+
}
|
|
41364
|
+
/**
|
|
41365
|
+
* ADMIN: Toggle token active status
|
|
41366
|
+
*/
|
|
41367
|
+
toggleTokenActive(tokenId) {
|
|
41368
|
+
return this.tokenBusinessService.toggleTokenActive(tokenId);
|
|
41369
|
+
}
|
|
41370
|
+
/**
|
|
41371
|
+
* ADMIN: Create new token
|
|
41372
|
+
*/
|
|
41373
|
+
createToken(tokenData) {
|
|
41374
|
+
return this.tokenBusinessService.createToken(tokenData);
|
|
41375
|
+
}
|
|
41376
|
+
/**
|
|
41377
|
+
* ADMIN: Update token
|
|
41378
|
+
*/
|
|
41379
|
+
updateToken(tokenId, tokenData) {
|
|
41380
|
+
return this.tokenBusinessService.updateToken(tokenId, tokenData);
|
|
41381
|
+
}
|
|
41382
|
+
/**
|
|
41383
|
+
* ADMIN: Set mainnet contract address
|
|
41384
|
+
*/
|
|
41385
|
+
setMainnetContract(tokenId, contractAddress, chainId) {
|
|
41386
|
+
return this.tokenBusinessService.setMainnetContract(tokenId, contractAddress, chainId);
|
|
41387
|
+
}
|
|
41388
|
+
/**
|
|
41389
|
+
* ADMIN: Toggle token metadata status
|
|
41390
|
+
*/
|
|
41391
|
+
toggleTokenMetadataStatus(metadataId) {
|
|
41392
|
+
return this.tokenBusinessService.toggleTokenMetadataStatus(metadataId);
|
|
41393
|
+
}
|
|
41394
|
+
/**
|
|
41395
|
+
* ADMIN: Create token type
|
|
41396
|
+
*/
|
|
41397
|
+
createTokenType(tokenType) {
|
|
41398
|
+
return this.tokenBusinessService.createTokenType(tokenType);
|
|
41399
|
+
}
|
|
41400
|
+
}
|
|
41401
|
+
|
|
41402
|
+
/**
|
|
41403
|
+
* Node.js HTTP Client adapter for PERS SDK
|
|
41404
|
+
*
|
|
41405
|
+
* Simple HTTP client for Node.js environments using native fetch (Node 18+) or a polyfill.
|
|
41406
|
+
* Works with Express, Next.js API routes, or any Node.js-based backend.
|
|
41407
|
+
*
|
|
41408
|
+
* @example
|
|
41409
|
+
* ```typescript
|
|
41410
|
+
* import { createPersSDK } from '@explorins/pers-sdk';
|
|
41411
|
+
* import { NodeHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
41412
|
+
*
|
|
41413
|
+
* const sdk = createPersSDK(
|
|
41414
|
+
* new NodeHttpClientAdapter(),
|
|
41415
|
+
* { environment: 'production', apiProjectKey: 'your-key' }
|
|
41416
|
+
* );
|
|
41417
|
+
*
|
|
41418
|
+
* // Use managers directly
|
|
41419
|
+
* await sdk.user.getCurrentUser();
|
|
41420
|
+
* ```
|
|
41421
|
+
*/
|
|
41422
|
+
class NodeHttpClientAdapter {
|
|
41423
|
+
async get(url, options = {}) {
|
|
41424
|
+
return this.request(url, 'GET', undefined, options);
|
|
41425
|
+
}
|
|
41426
|
+
async post(url, data = null, options = {}) {
|
|
41427
|
+
return this.request(url, 'POST', data, options);
|
|
41428
|
+
}
|
|
41429
|
+
async put(url, data = null, options = {}) {
|
|
41430
|
+
return this.request(url, 'PUT', data, options);
|
|
41431
|
+
}
|
|
41432
|
+
async delete(url, options = {}) {
|
|
41433
|
+
return this.request(url, 'DELETE', undefined, options);
|
|
41434
|
+
}
|
|
41435
|
+
async patch(url, data = null, options = {}) {
|
|
41436
|
+
return this.request(url, 'PATCH', data, options);
|
|
41437
|
+
}
|
|
41438
|
+
async request(url, method, data, options = {}) {
|
|
41439
|
+
// Use native fetch in Node 18+ or require a polyfill for older versions
|
|
41440
|
+
const fetchImpl = globalThis.fetch || require('node-fetch');
|
|
41441
|
+
const headers = {
|
|
41442
|
+
'Content-Type': 'application/json',
|
|
41443
|
+
'User-Agent': 'PERS-SDK-Node/1.0',
|
|
41444
|
+
...options.headers
|
|
41445
|
+
};
|
|
41446
|
+
const fetchOptions = {
|
|
41447
|
+
method,
|
|
41448
|
+
headers,
|
|
41449
|
+
...options
|
|
41450
|
+
};
|
|
41451
|
+
if (data !== undefined && method !== 'GET') {
|
|
41452
|
+
fetchOptions.body = JSON.stringify(data);
|
|
41453
|
+
}
|
|
41454
|
+
const response = await fetchImpl(url, fetchOptions);
|
|
41455
|
+
if (!response.ok) {
|
|
41456
|
+
// Extract backend error response if available
|
|
41457
|
+
let errorData;
|
|
41458
|
+
try {
|
|
41459
|
+
const contentType = response.headers.get('content-type');
|
|
41460
|
+
if (contentType?.includes('application/json')) {
|
|
41461
|
+
errorData = await response.json();
|
|
41462
|
+
}
|
|
41463
|
+
}
|
|
41464
|
+
catch {
|
|
41465
|
+
// If parsing fails, continue with basic error
|
|
41466
|
+
}
|
|
41467
|
+
// Create error with backend data attached
|
|
41468
|
+
const error = new Error(errorData?.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
41469
|
+
error.status = response.status;
|
|
41470
|
+
error.statusText = response.statusText;
|
|
41471
|
+
// Attach backend error response for extraction
|
|
41472
|
+
if (errorData) {
|
|
41473
|
+
Object.assign(error, errorData);
|
|
41474
|
+
}
|
|
41475
|
+
throw error;
|
|
41476
|
+
}
|
|
41477
|
+
// Handle different response types
|
|
41478
|
+
const contentType = response.headers.get('content-type');
|
|
41479
|
+
if (contentType?.includes('application/json')) {
|
|
41480
|
+
return response.json();
|
|
41481
|
+
}
|
|
41482
|
+
return response.text();
|
|
41483
|
+
}
|
|
41484
|
+
}
|
|
41485
|
+
|
|
41486
|
+
/**
|
|
41487
|
+
* Angular HTTP Client adapter for PERS SDK
|
|
41488
|
+
*
|
|
41489
|
+
* Simple bridge between Angular's HttpClient and PERS SDK's HttpClient interface.
|
|
41490
|
+
* Use this for easy integration with Angular applications without requiring Angular as a dependency.
|
|
41491
|
+
*
|
|
41492
|
+
* @example
|
|
41493
|
+
* ```typescript
|
|
41494
|
+
* import { inject } from '@angular/core';
|
|
41495
|
+
* import { HttpClient } from '@angular/common/http';
|
|
41496
|
+
* import { createPersSDK } from '@explorins/pers-sdk';
|
|
41497
|
+
* import { AngularHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
41498
|
+
*
|
|
41499
|
+
* const sdk = createPersSDK(
|
|
41500
|
+
* new AngularHttpClientAdapter(inject(HttpClient)),
|
|
41501
|
+
* { environment: 'production', apiProjectKey: 'your-key' }
|
|
41502
|
+
* );
|
|
41503
|
+
*
|
|
41504
|
+
* // Use managers directly
|
|
41505
|
+
* await sdk.user.getCurrentUser();
|
|
41506
|
+
* ```
|
|
41507
|
+
*/
|
|
41508
|
+
class AngularHttpClientAdapter {
|
|
41509
|
+
constructor(httpClient) {
|
|
41510
|
+
this.httpClient = httpClient;
|
|
41511
|
+
}
|
|
41512
|
+
async get(url, options = {}) {
|
|
41513
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41514
|
+
const request$ = this.httpClient.get(url, mergedOptions);
|
|
41515
|
+
return this.convertObservableToPromise(request$);
|
|
41516
|
+
}
|
|
41517
|
+
async post(url, data = null, options = {}) {
|
|
41518
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41519
|
+
const request$ = this.httpClient.post(url, data, mergedOptions);
|
|
41520
|
+
return this.convertObservableToPromise(request$);
|
|
41521
|
+
}
|
|
41522
|
+
async put(url, data = null, options = {}) {
|
|
41523
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41524
|
+
const request$ = this.httpClient.put(url, data, mergedOptions);
|
|
41525
|
+
return this.convertObservableToPromise(request$);
|
|
41526
|
+
}
|
|
41527
|
+
async delete(url, options = {}) {
|
|
41528
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41529
|
+
const request$ = this.httpClient.delete(url, mergedOptions);
|
|
41530
|
+
return this.convertObservableToPromise(request$);
|
|
41531
|
+
}
|
|
41532
|
+
async patch(url, data = null, options = {}) {
|
|
41533
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41534
|
+
const request$ = this.httpClient.patch(url, data, mergedOptions);
|
|
41535
|
+
return this.convertObservableToPromise(request$);
|
|
41536
|
+
}
|
|
41537
|
+
async convertObservableToPromise(observable) {
|
|
41538
|
+
// Dynamically import firstValueFrom to avoid build-time dependency
|
|
41539
|
+
const { firstValueFrom } = await Promise.resolve().then(function () { return require('./index-CzLxqePZ.js'); }).then(function (n) { return n.index; });
|
|
41540
|
+
return firstValueFrom(observable);
|
|
41541
|
+
}
|
|
41542
|
+
mergeOptions(options) {
|
|
41543
|
+
// Create headers object without importing HttpHeaders
|
|
41544
|
+
const defaultHeaders = { 'Content-Type': 'application/json' };
|
|
41545
|
+
const headers = { ...defaultHeaders, ...options.headers };
|
|
41546
|
+
return {
|
|
41547
|
+
headers,
|
|
41548
|
+
observe: 'body',
|
|
41549
|
+
...options
|
|
41550
|
+
};
|
|
41551
|
+
}
|
|
41552
|
+
}
|
|
41553
|
+
|
|
41554
|
+
/**
|
|
41555
|
+
* Browser Fetch HTTP Client adapter for PERS SDK
|
|
41556
|
+
*
|
|
41557
|
+
* Simple fetch-based HTTP client for browser environments.
|
|
41558
|
+
* Works with vanilla JavaScript, React, Vue, or any browser-based framework.
|
|
41559
|
+
*
|
|
41560
|
+
* @example
|
|
41561
|
+
* ```typescript
|
|
41562
|
+
* import { createPersSDK } from '@explorins/pers-sdk';
|
|
41563
|
+
* import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
41564
|
+
*
|
|
41565
|
+
* const sdk = createPersSDK(
|
|
41566
|
+
* new BrowserFetchClientAdapter(),
|
|
41567
|
+
* { environment: 'production', apiProjectKey: 'your-key' }
|
|
41568
|
+
* );
|
|
41569
|
+
*
|
|
41570
|
+
* // Use managers directly
|
|
41571
|
+
* await sdk.user.getCurrentUser();
|
|
41572
|
+
* ```
|
|
41573
|
+
*/
|
|
41574
|
+
class BrowserFetchClientAdapter {
|
|
41575
|
+
async get(url, options = {}) {
|
|
41576
|
+
return this.request(url, 'GET', undefined, options);
|
|
41577
|
+
}
|
|
41578
|
+
async post(url, data = null, options = {}) {
|
|
41579
|
+
return this.request(url, 'POST', data, options);
|
|
41580
|
+
}
|
|
41581
|
+
async put(url, data = null, options = {}) {
|
|
41582
|
+
return this.request(url, 'PUT', data, options);
|
|
41583
|
+
}
|
|
41584
|
+
async delete(url, options = {}) {
|
|
41585
|
+
return this.request(url, 'DELETE', undefined, options);
|
|
41586
|
+
}
|
|
41587
|
+
async patch(url, data = null, options = {}) {
|
|
41588
|
+
return this.request(url, 'PATCH', data, options);
|
|
41589
|
+
}
|
|
41590
|
+
async request(url, method, data, options = {}) {
|
|
41591
|
+
const headers = {
|
|
41592
|
+
'Content-Type': 'application/json',
|
|
41593
|
+
...options.headers
|
|
41594
|
+
};
|
|
41595
|
+
const fetchOptions = {
|
|
41596
|
+
method,
|
|
41597
|
+
headers,
|
|
41598
|
+
...options
|
|
41599
|
+
};
|
|
41600
|
+
if (data !== undefined && method !== 'GET') {
|
|
41601
|
+
fetchOptions.body = JSON.stringify(data);
|
|
41602
|
+
}
|
|
41603
|
+
const response = await fetch(url, fetchOptions);
|
|
41604
|
+
if (!response.ok) {
|
|
41605
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
41606
|
+
}
|
|
41607
|
+
// Handle different response types
|
|
41608
|
+
const contentType = response.headers.get('content-type');
|
|
41609
|
+
if (contentType?.includes('application/json')) {
|
|
41610
|
+
return response.json();
|
|
41611
|
+
}
|
|
41612
|
+
return response.text();
|
|
41613
|
+
}
|
|
41614
|
+
}
|
|
41615
|
+
|
|
40278
41616
|
/**
|
|
40279
41617
|
* React hook for loading and managing token balances
|
|
40280
41618
|
*
|
|
@@ -41438,12 +42776,38 @@ const useBusiness = () => {
|
|
|
41438
42776
|
throw error;
|
|
41439
42777
|
}
|
|
41440
42778
|
}, [sdk, isInitialized]);
|
|
41441
|
-
|
|
42779
|
+
/**
|
|
42780
|
+
* Admin: Get all businesses with optional filtering
|
|
42781
|
+
*
|
|
42782
|
+
* @param options - Filter and pagination options
|
|
42783
|
+
* @returns Promise resolving to paginated businesses
|
|
42784
|
+
*
|
|
42785
|
+
* @example
|
|
42786
|
+
* ```typescript
|
|
42787
|
+
* // Get all businesses
|
|
42788
|
+
* const { data } = await getBusinesses();
|
|
42789
|
+
*
|
|
42790
|
+
* // Filter by tag
|
|
42791
|
+
* const tagged = await getBusinesses({ tag: 'restaurant' });
|
|
42792
|
+
*
|
|
42793
|
+
* // Multiple tags (OR match)
|
|
42794
|
+
* const multiTag = await getBusinesses({ tag: ['restaurant', 'cafe'] });
|
|
42795
|
+
*
|
|
42796
|
+
* // With search and pagination
|
|
42797
|
+
* const filtered = await getBusinesses({
|
|
42798
|
+
* search: 'coffee',
|
|
42799
|
+
* active: true,
|
|
42800
|
+
* page: 1,
|
|
42801
|
+
* limit: 20
|
|
42802
|
+
* });
|
|
42803
|
+
* ```
|
|
42804
|
+
*/
|
|
42805
|
+
const getBusinesses = react.useCallback(async (options) => {
|
|
41442
42806
|
if (!isInitialized || !sdk) {
|
|
41443
42807
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
41444
42808
|
}
|
|
41445
42809
|
try {
|
|
41446
|
-
const result = await sdk.businesses.getBusinesses();
|
|
42810
|
+
const result = await sdk.businesses.getBusinesses(options);
|
|
41447
42811
|
return result;
|
|
41448
42812
|
}
|
|
41449
42813
|
catch (error) {
|
|
@@ -41565,11 +42929,15 @@ const useCampaigns = () => {
|
|
|
41565
42929
|
* include: ['triggerSources', 'businesses']
|
|
41566
42930
|
* });
|
|
41567
42931
|
*
|
|
41568
|
-
* // Filter by
|
|
41569
|
-
* const { data } = await getActiveCampaigns({
|
|
42932
|
+
* // Filter by date range
|
|
42933
|
+
* const { data } = await getActiveCampaigns({
|
|
42934
|
+
* startDate: '2026-05-01',
|
|
42935
|
+
* endDate: '2026-12-31'
|
|
42936
|
+
* });
|
|
41570
42937
|
*
|
|
41571
|
-
* //
|
|
41572
|
-
* const { data } = await getActiveCampaigns({
|
|
42938
|
+
* // Get campaigns with claim count
|
|
42939
|
+
* const { data } = await getActiveCampaigns({ include: ['claimCount'] });
|
|
42940
|
+
* data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
|
|
41573
42941
|
* ```
|
|
41574
42942
|
*/
|
|
41575
42943
|
const getActiveCampaigns = react.useCallback(async (options) => {
|
|
@@ -41592,7 +42960,7 @@ const useCampaigns = () => {
|
|
|
41592
42960
|
* Get campaign by ID with optional include relations
|
|
41593
42961
|
*
|
|
41594
42962
|
* @param campaignId - The campaign ID
|
|
41595
|
-
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
42963
|
+
* @param include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
|
|
41596
42964
|
* @returns Promise resolving to campaign data
|
|
41597
42965
|
*
|
|
41598
42966
|
* @example
|
|
@@ -41600,6 +42968,10 @@ const useCampaigns = () => {
|
|
|
41600
42968
|
* // Get campaign with all related data
|
|
41601
42969
|
* const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
|
|
41602
42970
|
* console.log('Trigger sources:', campaign.included?.triggerSources);
|
|
42971
|
+
*
|
|
42972
|
+
* // Get campaign with claim count
|
|
42973
|
+
* const campaign = await getCampaignById('campaign-123', ['claimCount']);
|
|
42974
|
+
* console.log('Total claims:', campaign.claimCount);
|
|
41603
42975
|
* ```
|
|
41604
42976
|
*/
|
|
41605
42977
|
const getCampaignById = react.useCallback(async (campaignId, include) => {
|
|
@@ -41751,8 +43123,13 @@ const useCampaigns = () => {
|
|
|
41751
43123
|
* // Get only active campaigns with trigger sources
|
|
41752
43124
|
* const { data } = await getAllCampaigns({ active: true, include: ['triggerSources'] });
|
|
41753
43125
|
*
|
|
41754
|
-
* //
|
|
41755
|
-
* const { data } = await getAllCampaigns({
|
|
43126
|
+
* // Filter by date range with claim count
|
|
43127
|
+
* const { data } = await getAllCampaigns({
|
|
43128
|
+
* startDate: '2026-05-01',
|
|
43129
|
+
* endDate: '2026-12-31',
|
|
43130
|
+
* include: ['claimCount']
|
|
43131
|
+
* });
|
|
43132
|
+
* data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
|
|
41756
43133
|
*
|
|
41757
43134
|
* // With pagination and sorting
|
|
41758
43135
|
* const { data } = await getAllCampaigns({
|
|
@@ -41779,7 +43156,16 @@ const useCampaigns = () => {
|
|
|
41779
43156
|
/**
|
|
41780
43157
|
* Admin: Get campaign claims with optional filters and include relations
|
|
41781
43158
|
*
|
|
41782
|
-
* @param filters - Optional filters for campaign, user,
|
|
43159
|
+
* @param filters - Optional filters for campaign, user, business, and date range
|
|
43160
|
+
* @param filters.campaignId - Filter by specific campaign ID
|
|
43161
|
+
* @param filters.userId - Filter by user ID
|
|
43162
|
+
* @param filters.businessId - Filter by business ID
|
|
43163
|
+
* @param filters.dateFrom - Filter claims created on or after this date (ISO 8601 format)
|
|
43164
|
+
* @param filters.dateTo - Filter claims created on or before this date (ISO 8601 format)
|
|
43165
|
+
* @param filters.page - Page number (1-indexed)
|
|
43166
|
+
* @param filters.limit - Items per page
|
|
43167
|
+
* @param filters.sortBy - Field to sort by
|
|
43168
|
+
* @param filters.sortOrder - Sort direction
|
|
41783
43169
|
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
41784
43170
|
* @returns Promise resolving to paginated campaign claims
|
|
41785
43171
|
*
|
|
@@ -41790,6 +43176,12 @@ const useCampaigns = () => {
|
|
|
41790
43176
|
*
|
|
41791
43177
|
* // Get claims for a specific campaign
|
|
41792
43178
|
* const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
|
|
43179
|
+
*
|
|
43180
|
+
* // Filter by date range
|
|
43181
|
+
* const { data } = await getCampaignClaims({
|
|
43182
|
+
* dateFrom: '2026-01-01',
|
|
43183
|
+
* dateTo: '2026-03-31'
|
|
43184
|
+
* }, ['campaign']);
|
|
41793
43185
|
* ```
|
|
41794
43186
|
*/
|
|
41795
43187
|
const getCampaignClaims = react.useCallback(async (filters, include) => {
|
|
@@ -41945,6 +43337,9 @@ const useRedemptions = () => {
|
|
|
41945
43337
|
* @param options - Filter and pagination options
|
|
41946
43338
|
* @param options.active - Filter by active status (Admin only)
|
|
41947
43339
|
* @param options.search - Free text search across name, description, tags
|
|
43340
|
+
* @param options.startDate - Filter redemptions starting on or after this date
|
|
43341
|
+
* @param options.endDate - Filter redemptions ending on or before this date
|
|
43342
|
+
* @param options.include - Relations to include: 'redeemCount'
|
|
41948
43343
|
* @param options.page - Page number (1-indexed)
|
|
41949
43344
|
* @param options.limit - Results per page
|
|
41950
43345
|
* @param options.sortBy - Field to sort by ('name' | 'createdAt')
|
|
@@ -41955,12 +43350,21 @@ const useRedemptions = () => {
|
|
|
41955
43350
|
* const { data: offers } = await getRedemptions();
|
|
41956
43351
|
* ```
|
|
41957
43352
|
*
|
|
41958
|
-
* @example
|
|
43353
|
+
* @example Filter by date range
|
|
43354
|
+
* ```typescript
|
|
43355
|
+
* const { data: dateFiltered } = await getRedemptions({
|
|
43356
|
+
* startDate: '2026-05-01',
|
|
43357
|
+
* endDate: '2026-06-30'
|
|
43358
|
+
* });
|
|
43359
|
+
* ```
|
|
43360
|
+
*
|
|
43361
|
+
* @example Get redemptions with redeem count
|
|
41959
43362
|
* ```typescript
|
|
41960
|
-
* const { data:
|
|
43363
|
+
* const { data: offers } = await getRedemptions({ include: ['redeemCount'] });
|
|
43364
|
+
* offers.forEach(o => console.log(`${o.name}: ${o.redeemCount} redeems`));
|
|
41961
43365
|
* ```
|
|
41962
43366
|
*
|
|
41963
|
-
* @example
|
|
43367
|
+
* @example Search with pagination
|
|
41964
43368
|
* ```typescript
|
|
41965
43369
|
* const { data, pagination } = await getRedemptions({
|
|
41966
43370
|
* search: 'discount',
|
|
@@ -42125,7 +43529,17 @@ const useRedemptions = () => {
|
|
|
42125
43529
|
*
|
|
42126
43530
|
* Retrieves all redemption redeems across the platform with filtering.
|
|
42127
43531
|
*
|
|
42128
|
-
* @param filters - Optional filters for user, redemption, and pagination
|
|
43532
|
+
* @param filters - Optional filters for user, redemption, date range, and pagination
|
|
43533
|
+
* @param filters.userId - Filter by user ID
|
|
43534
|
+
* @param filters.redemptionId - Filter by redemption offer ID
|
|
43535
|
+
* @param filters.businessId - Filter by business ID (admin only)
|
|
43536
|
+
* @param filters.status - Filter by processing status (PENDING, PROCESSING, COMPLETED, FAILED)
|
|
43537
|
+
* @param filters.dateFrom - Filter redeems created on or after this date (ISO 8601 format)
|
|
43538
|
+
* @param filters.dateTo - Filter redeems created on or before this date (ISO 8601 format)
|
|
43539
|
+
* @param filters.page - Page number (1-indexed)
|
|
43540
|
+
* @param filters.limit - Items per page
|
|
43541
|
+
* @param filters.sortBy - Field to sort by
|
|
43542
|
+
* @param filters.sortOrder - Sort direction
|
|
42129
43543
|
* @param include - Relations to include: 'redemption', 'user', 'business'
|
|
42130
43544
|
* @returns Promise resolving to paginated redemption redeems
|
|
42131
43545
|
*
|
|
@@ -42139,6 +43553,12 @@ const useRedemptions = () => {
|
|
|
42139
43553
|
* { userId: 'user-123' },
|
|
42140
43554
|
* ['redemption']
|
|
42141
43555
|
* );
|
|
43556
|
+
*
|
|
43557
|
+
* // Filter by date range
|
|
43558
|
+
* const { data } = await getRedemptionRedeems({
|
|
43559
|
+
* dateFrom: '2026-01-01',
|
|
43560
|
+
* dateTo: '2026-03-31'
|
|
43561
|
+
* }, ['user', 'redemption']);
|
|
42142
43562
|
* ```
|
|
42143
43563
|
*/
|
|
42144
43564
|
const getRedemptionRedeems = react.useCallback(async (filters, include) => {
|
|
@@ -43621,112 +45041,125 @@ const useTriggerSources = () => {
|
|
|
43621
45041
|
};
|
|
43622
45042
|
};
|
|
43623
45043
|
|
|
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
45044
|
exports.ANALYTICS_GROUP_BY_TYPES = ANALYTICS_GROUP_BY_TYPES;
|
|
43695
45045
|
exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
|
|
45046
|
+
exports.AUTH_STORAGE_KEYS = AUTH_STORAGE_KEYS;
|
|
45047
|
+
exports.AnalyticsApi = AnalyticsApi;
|
|
45048
|
+
exports.AnalyticsManager = AnalyticsManager;
|
|
45049
|
+
exports.AnalyticsService = AnalyticsService;
|
|
45050
|
+
exports.AngularHttpClientAdapter = AngularHttpClientAdapter;
|
|
45051
|
+
exports.ApiErrorDetector = ApiErrorDetector;
|
|
45052
|
+
exports.ApiKeyApi = ApiKeyApi;
|
|
45053
|
+
exports.ApiKeyManager = ApiKeyManager;
|
|
43696
45054
|
exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
|
|
43697
45055
|
exports.AuditEntityTypes = AuditEntityTypes;
|
|
45056
|
+
exports.AuthApi = AuthApi;
|
|
45057
|
+
exports.AuthManager = AuthManager;
|
|
45058
|
+
exports.AuthService = AuthService;
|
|
45059
|
+
exports.AuthTokenManager = AuthTokenManager;
|
|
43698
45060
|
exports.AuthenticationError = AuthenticationError;
|
|
45061
|
+
exports.BaseTokenService = BaseTokenService;
|
|
45062
|
+
exports.BrowserFetchClientAdapter = BrowserFetchClientAdapter;
|
|
45063
|
+
exports.BusinessApi = BusinessApi;
|
|
45064
|
+
exports.BusinessManager = BusinessManager;
|
|
45065
|
+
exports.BusinessMembershipApi = BusinessMembershipApi;
|
|
45066
|
+
exports.BusinessMembershipService = BusinessMembershipService;
|
|
45067
|
+
exports.BusinessService = BusinessService;
|
|
45068
|
+
exports.CacheService = CacheService;
|
|
45069
|
+
exports.CacheTTL = CacheTTL;
|
|
45070
|
+
exports.CampaignApi = CampaignApi;
|
|
45071
|
+
exports.CampaignManager = CampaignManager;
|
|
45072
|
+
exports.CampaignService = CampaignService;
|
|
43699
45073
|
exports.ChainTypes = ChainTypes;
|
|
43700
45074
|
exports.ClientTransactionType = ClientTransactionType;
|
|
45075
|
+
exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
|
|
45076
|
+
exports.DPOP_STORAGE_KEYS = DPOP_STORAGE_KEYS;
|
|
45077
|
+
exports.DPoPManager = DPoPManager;
|
|
45078
|
+
exports.DefaultAuthProvider = DefaultAuthProvider;
|
|
43701
45079
|
exports.Domains = Domains;
|
|
45080
|
+
exports.DonationApi = DonationApi;
|
|
45081
|
+
exports.DonationManager = DonationManager;
|
|
45082
|
+
exports.DonationService = DonationService;
|
|
43702
45083
|
exports.EntityTypes = EntityTypes;
|
|
43703
45084
|
exports.ErrorDomains = ErrorDomains;
|
|
43704
45085
|
exports.ErrorUtils = ErrorUtils;
|
|
45086
|
+
exports.FileApi = FileApi;
|
|
45087
|
+
exports.FileManager = FileManager;
|
|
45088
|
+
exports.FileService = FileService;
|
|
45089
|
+
exports.IndexedDBTokenStorage = IndexedDBTokenStorage;
|
|
45090
|
+
exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
|
|
45091
|
+
exports.LogoutRequired = LogoutRequired;
|
|
43705
45092
|
exports.MEMBERSHIP_ROLE_HIERARCHY = MEMBERSHIP_ROLE_HIERARCHY;
|
|
45093
|
+
exports.MemoryTokenStorage = MemoryTokenStorage;
|
|
43706
45094
|
exports.NativeTokenTypes = NativeTokenTypes;
|
|
45095
|
+
exports.NetworkError = NetworkError;
|
|
45096
|
+
exports.NodeHttpClientAdapter = NodeHttpClientAdapter;
|
|
45097
|
+
exports.PaymentApi = PurchaseApi;
|
|
45098
|
+
exports.PaymentService = PaymentService;
|
|
45099
|
+
exports.PersApiClient = PersApiClient;
|
|
43707
45100
|
exports.PersApiError = PersApiError;
|
|
45101
|
+
exports.PersEventEmitter = PersEventEmitter;
|
|
45102
|
+
exports.PersEventsClient = PersEventsClient;
|
|
45103
|
+
exports.PersSDK = PersSDK;
|
|
43708
45104
|
exports.PersSDKProvider = PersSDKProvider;
|
|
45105
|
+
exports.ProviderTokenRefreshNeeded = ProviderTokenRefreshNeeded;
|
|
45106
|
+
exports.PurchaseManager = PurchaseManager;
|
|
43709
45107
|
exports.QUERY_OPERATOR_SQL_MAP = QUERY_OPERATOR_SQL_MAP;
|
|
43710
45108
|
exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
|
|
43711
45109
|
exports.ReactNativeHttpClient = ReactNativeHttpClient;
|
|
43712
45110
|
exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
|
|
45111
|
+
exports.RedemptionApi = RedemptionApi;
|
|
45112
|
+
exports.RedemptionManager = RedemptionManager;
|
|
45113
|
+
exports.RedemptionService = RedemptionService;
|
|
45114
|
+
exports.SDK_NAME = SDK_NAME;
|
|
45115
|
+
exports.SDK_USER_AGENT = SDK_USER_AGENT;
|
|
45116
|
+
exports.SDK_VERSION = SDK_VERSION;
|
|
43713
45117
|
exports.SOURCE_LOGIC_TYPES = SOURCE_LOGIC_TYPES;
|
|
43714
45118
|
exports.SOURCE_LOGIC_TYPE_VALUES = SOURCE_LOGIC_TYPE_VALUES;
|
|
45119
|
+
exports.SdkErrorCodes = SdkErrorCodes;
|
|
43715
45120
|
exports.SigningStatus = SigningStatus;
|
|
45121
|
+
exports.StaticJwtAuthProvider = StaticJwtAuthProvider;
|
|
43716
45122
|
exports.TOKEN_VALIDITY_TYPE_VALUES = TOKEN_VALIDITY_TYPE_VALUES;
|
|
43717
45123
|
exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
|
|
43718
45124
|
exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
|
|
43719
45125
|
exports.TRIGGER_SOURCE_TYPES = TRIGGER_SOURCE_TYPES;
|
|
43720
45126
|
exports.TRIGGER_SOURCE_TYPE_VALUES = TRIGGER_SOURCE_TYPE_VALUES;
|
|
45127
|
+
exports.TenantApi = TenantApi;
|
|
45128
|
+
exports.TenantManager = TenantManager;
|
|
45129
|
+
exports.TenantService = TenantService;
|
|
45130
|
+
exports.TokenApi = TokenApi;
|
|
45131
|
+
exports.TokenManager = TokenManager;
|
|
45132
|
+
exports.TokenRefreshNeeded = TokenRefreshNeeded;
|
|
45133
|
+
exports.TokenSDK = TokenSDK;
|
|
45134
|
+
exports.TokenService = TokenService;
|
|
43721
45135
|
exports.TokenValidityTypes = TokenValidityTypes;
|
|
45136
|
+
exports.TransactionApi = TransactionApi;
|
|
45137
|
+
exports.TransactionManager = TransactionManager;
|
|
45138
|
+
exports.TransactionService = TransactionService;
|
|
45139
|
+
exports.TriggerSourceApi = TriggerSourceApi;
|
|
45140
|
+
exports.TriggerSourceManager = TriggerSourceManager;
|
|
45141
|
+
exports.TriggerSourceService = TriggerSourceService;
|
|
45142
|
+
exports.UserApi = UserApi;
|
|
45143
|
+
exports.UserManager = UserManager;
|
|
45144
|
+
exports.UserService = UserService;
|
|
45145
|
+
exports.UserStatusApi = UserStatusApi;
|
|
45146
|
+
exports.UserStatusManager = UserStatusManager;
|
|
45147
|
+
exports.UserStatusService = UserStatusService;
|
|
43722
45148
|
exports.VALID_BUSINESS_MEMBERSHIP_RELATIONS = VALID_BUSINESS_MEMBERSHIP_RELATIONS;
|
|
43723
45149
|
exports.VALID_CAMPAIGN_CLAIM_RELATIONS = VALID_CAMPAIGN_CLAIM_RELATIONS;
|
|
43724
45150
|
exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
|
|
43725
45151
|
exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
|
|
45152
|
+
exports.VALID_REDEMPTION_RELATIONS = VALID_REDEMPTION_RELATIONS;
|
|
43726
45153
|
exports.VALID_RELATIONS = VALID_RELATIONS;
|
|
43727
45154
|
exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
|
|
43728
45155
|
exports.VALID_USER_RELATIONS = VALID_USER_RELATIONS;
|
|
45156
|
+
exports.WalletEventsManager = WalletEventsManager;
|
|
45157
|
+
exports.WebDPoPCryptoProvider = WebDPoPCryptoProvider;
|
|
45158
|
+
exports.WebhookApi = WebhookApi;
|
|
45159
|
+
exports.WebhookManager = WebhookManager;
|
|
45160
|
+
exports.WebhookService = WebhookService;
|
|
43729
45161
|
exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;
|
|
45162
|
+
exports.buildApiRoot = buildApiRoot;
|
|
43730
45163
|
exports.buildBurnRequest = buildBurnRequest;
|
|
43731
45164
|
exports.buildMintRequest = buildMintRequest;
|
|
43732
45165
|
exports.buildPOSBurnRequest = buildPOSBurnRequest;
|
|
@@ -43735,14 +45168,22 @@ exports.buildPaginationParams = buildPaginationParams;
|
|
|
43735
45168
|
exports.buildPendingTransactionData = buildPendingTransactionData;
|
|
43736
45169
|
exports.buildSubmissionRequest = buildSubmissionRequest;
|
|
43737
45170
|
exports.buildTransferRequest = buildTransferRequest;
|
|
45171
|
+
exports.buildWalletEventsWsUrl = buildWalletEventsWsUrl;
|
|
45172
|
+
exports.createPersEventsClient = createPersEventsClient;
|
|
45173
|
+
exports.createPersSDK = createPersSDK;
|
|
43738
45174
|
exports.createReactNativeAuthProvider = createReactNativeAuthProvider;
|
|
43739
45175
|
exports.createUserIdentifierObject = createUserIdentifierObject;
|
|
45176
|
+
exports.createUserStatusSDK = createUserStatusSDK;
|
|
45177
|
+
exports.detectEnvironment = detectEnvironment;
|
|
43740
45178
|
exports.detectIdentifierType = detectIdentifierType;
|
|
45179
|
+
exports.environment = environment;
|
|
43741
45180
|
exports.extractData = extractData;
|
|
43742
45181
|
exports.extractDeadlineFromSigningData = extractDeadlineFromSigningData;
|
|
43743
45182
|
exports.extractPagination = extractPagination;
|
|
43744
45183
|
exports.fetchAllPages = fetchAllPages;
|
|
43745
45184
|
exports.getMetadataFromTokenUnitResponse = getMetadataFromTokenUnitResponse;
|
|
45185
|
+
exports.getSigningUrl = getSigningUrl;
|
|
45186
|
+
exports.globalCacheService = globalCacheService;
|
|
43746
45187
|
exports.hasMinimumRole = hasMinimumRole;
|
|
43747
45188
|
exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
|
|
43748
45189
|
exports.isConnectedMessage = isConnectedMessage;
|
|
@@ -43758,9 +45199,14 @@ exports.isValidBusinessMembershipRelation = isValidBusinessMembershipRelation;
|
|
|
43758
45199
|
exports.isValidCampaignClaimRelation = isValidCampaignClaimRelation;
|
|
43759
45200
|
exports.isValidCampaignRelation = isValidCampaignRelation;
|
|
43760
45201
|
exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
|
|
45202
|
+
exports.isValidRedemptionRelation = isValidRedemptionRelation;
|
|
43761
45203
|
exports.isValidRelation = isValidRelation;
|
|
43762
45204
|
exports.isValidTransactionRelation = isValidTransactionRelation;
|
|
43763
45205
|
exports.isValidUserRelation = isValidUserRelation;
|
|
45206
|
+
exports.mergeWithDefaults = mergeWithDefaults;
|
|
45207
|
+
exports.needsExternalSigning = needsExternalSigning;
|
|
45208
|
+
exports.needsSubmission = needsSubmission;
|
|
45209
|
+
exports.normalizeFilters = normalizeFilters;
|
|
43764
45210
|
exports.normalizeToPaginated = normalizeToPaginated;
|
|
43765
45211
|
exports.registerIdentifierType = registerIdentifierType;
|
|
43766
45212
|
exports.testnetPrefix = testnetPrefix;
|
|
@@ -43785,4 +45231,5 @@ exports.useTriggerSources = useTriggerSources;
|
|
|
43785
45231
|
exports.useUserStatus = useUserStatus;
|
|
43786
45232
|
exports.useUsers = useUsers;
|
|
43787
45233
|
exports.useWeb3 = useWeb3;
|
|
45234
|
+
exports.warnIfProblematicEnvironment = warnIfProblematicEnvironment;
|
|
43788
45235
|
//# sourceMappingURL=index.js.map
|