@explorins/pers-sdk-react-native 2.1.13 → 2.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -14
- 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 +5 -17
- 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 +12 -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 +28 -5
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +58 -1
- 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/useTransactionSigner.d.ts.map +1 -1
- package/dist/hooks/useTransactionSigner.js +38 -11
- 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 +1896 -317
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +7 -17
- package/dist/providers/rn-dpop-provider.d.ts.map +1 -1
- package/dist/providers/rn-dpop-provider.js +48 -12
- package/package.json +3 -3
- package/src/hooks/index.ts +5 -2
- package/src/hooks/useAnalytics.ts +1 -1
- package/src/hooks/useAuth.ts +6 -20
- package/src/hooks/useBusiness.ts +32 -3
- package/src/hooks/useCampaigns.ts +49 -12
- package/src/hooks/useDonations.ts +1 -1
- package/src/hooks/usePurchases.ts +1 -1
- package/src/hooks/useRedemptions.ts +91 -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/useTransactionSigner.ts +41 -10
- 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 +8 -17
- package/src/providers/rn-dpop-provider.ts +57 -11
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,10 +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)}`);
|
|
6032
|
+
if (options?.search)
|
|
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
|
+
}
|
|
5941
6042
|
if (options?.sortBy)
|
|
5942
6043
|
params.push(`sortBy=${options.sortBy}`);
|
|
5943
6044
|
if (options?.sortOrder)
|
|
@@ -5951,7 +6052,7 @@ class CampaignApi {
|
|
|
5951
6052
|
* NEW: /campaigns/{id}
|
|
5952
6053
|
*
|
|
5953
6054
|
* @param id - Campaign UUID
|
|
5954
|
-
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
6055
|
+
* @param include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
|
|
5955
6056
|
*
|
|
5956
6057
|
* @example
|
|
5957
6058
|
* ```typescript
|
|
@@ -5959,6 +6060,10 @@ class CampaignApi {
|
|
|
5959
6060
|
* const campaign = await api.getCampaignById('123', ['triggerSources', 'businesses']);
|
|
5960
6061
|
* console.log(campaign.included?.triggerSources);
|
|
5961
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);
|
|
5962
6067
|
* ```
|
|
5963
6068
|
*/
|
|
5964
6069
|
async getCampaignById(id, include) {
|
|
@@ -6207,6 +6312,10 @@ class CampaignApi {
|
|
|
6207
6312
|
params.set('userId', filters.userId);
|
|
6208
6313
|
if (filters?.businessId)
|
|
6209
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);
|
|
6210
6319
|
// Add include parameter
|
|
6211
6320
|
if (include && include.length > 0) {
|
|
6212
6321
|
include.forEach(relation => params.append('include', relation));
|
|
@@ -6304,13 +6413,7 @@ class CampaignService {
|
|
|
6304
6413
|
/**
|
|
6305
6414
|
* Get campaigns with pagination support
|
|
6306
6415
|
* Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all
|
|
6307
|
-
* @
|
|
6308
|
-
* @param options.businessId - Filter by business engagement
|
|
6309
|
-
* @param options.page - Page number (1-based, default: 1)
|
|
6310
|
-
* @param options.limit - Items per page (default: 50)
|
|
6311
|
-
* @param options.sortBy - Sort field
|
|
6312
|
-
* @param options.sortOrder - Sort direction
|
|
6313
|
-
* @param options.include - Relations to include: 'triggerSources', 'businesses'
|
|
6416
|
+
* @see CampaignFilterOptions for available filter options
|
|
6314
6417
|
*/
|
|
6315
6418
|
async getCampaigns(options) {
|
|
6316
6419
|
return this.campaignApi.getCampaigns(options);
|
|
@@ -6410,7 +6513,7 @@ class CampaignService {
|
|
|
6410
6513
|
}
|
|
6411
6514
|
/**
|
|
6412
6515
|
* ADMIN: Get campaign claims with optional filters and pagination
|
|
6413
|
-
* @param filters - Filter and pagination options
|
|
6516
|
+
* @param filters - Filter and pagination options including date range
|
|
6414
6517
|
* @param include - Optional relations to include (campaign, user, business)
|
|
6415
6518
|
*/
|
|
6416
6519
|
async getCampaignClaims(filters, include) {
|
|
@@ -6480,15 +6583,50 @@ class RedemptionApi {
|
|
|
6480
6583
|
* - Public users: Get active redemptions only
|
|
6481
6584
|
* - Admin users: Get all redemptions with optional filtering
|
|
6482
6585
|
*
|
|
6483
|
-
* @param options - Filter and pagination options (active, adminAccess, page, limit, sortBy, sortOrder)
|
|
6586
|
+
* @param options - Filter and pagination options (active, search, adminAccess, startDate, endDate, include, page, limit, sortBy, sortOrder)
|
|
6484
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
|
+
* ```
|
|
6485
6600
|
*/
|
|
6486
6601
|
async getRedemptions(options) {
|
|
6487
|
-
const params =
|
|
6488
|
-
|
|
6489
|
-
|
|
6602
|
+
const params = [];
|
|
6603
|
+
// Always include pagination params to ensure paginated response
|
|
6604
|
+
params.push(`page=${options?.page || 1}`);
|
|
6605
|
+
params.push(`limit=${options?.limit || 50}`);
|
|
6606
|
+
if (options?.active !== undefined)
|
|
6607
|
+
params.push(`active=${options.active}`);
|
|
6608
|
+
if (options?.search)
|
|
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)}`));
|
|
6490
6614
|
}
|
|
6491
|
-
|
|
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
|
+
}
|
|
6623
|
+
if (options?.sortBy)
|
|
6624
|
+
params.push(`sortBy=${options.sortBy}`);
|
|
6625
|
+
if (options?.sortOrder)
|
|
6626
|
+
params.push(`sortOrder=${options.sortOrder}`);
|
|
6627
|
+
if (options?.include?.length)
|
|
6628
|
+
params.push(`include=${options.include.join(',')}`);
|
|
6629
|
+
const response = await this.apiClient.get(`${this.basePath}?${params.join('&')}`);
|
|
6492
6630
|
return normalizeToPaginated(response);
|
|
6493
6631
|
}
|
|
6494
6632
|
/**
|
|
@@ -6507,9 +6645,20 @@ class RedemptionApi {
|
|
|
6507
6645
|
* PUBLIC: Get redemption by ID
|
|
6508
6646
|
*
|
|
6509
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
|
+
* ```
|
|
6510
6658
|
*/
|
|
6511
|
-
async getRedemptionById(id) {
|
|
6512
|
-
|
|
6659
|
+
async getRedemptionById(id, include) {
|
|
6660
|
+
const params = include?.length ? `?include=${include.join(',')}` : '';
|
|
6661
|
+
return this.apiClient.get(`${this.basePath}/${id}${params}`);
|
|
6513
6662
|
}
|
|
6514
6663
|
/**
|
|
6515
6664
|
* PUBLIC: Get available supply for redemption
|
|
@@ -6546,7 +6695,7 @@ class RedemptionApi {
|
|
|
6546
6695
|
* - Users: See only their own redeems (userId/businessId filters ignored)
|
|
6547
6696
|
* - Admins: Can filter by userId, businessId, redemptionId, or status
|
|
6548
6697
|
*
|
|
6549
|
-
* @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)
|
|
6550
6699
|
* @param include - Optional relations to include (redemption, user, business, transactions)
|
|
6551
6700
|
* @returns Paginated response with redemption redeems
|
|
6552
6701
|
*/
|
|
@@ -6562,6 +6711,12 @@ class RedemptionApi {
|
|
|
6562
6711
|
if (filters?.status) {
|
|
6563
6712
|
params.append('status', filters.status);
|
|
6564
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
|
+
}
|
|
6565
6720
|
// Add include parameter
|
|
6566
6721
|
if (include && include.length > 0) {
|
|
6567
6722
|
include.forEach(relation => params.append('include', relation));
|
|
@@ -6578,6 +6733,10 @@ class RedemptionApi {
|
|
|
6578
6733
|
params.append('businessId', filters.businessId);
|
|
6579
6734
|
if (filters?.status)
|
|
6580
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);
|
|
6581
6740
|
// Add include parameter
|
|
6582
6741
|
if (include && include.length > 0) {
|
|
6583
6742
|
include.forEach(relation => params.append('include', relation));
|
|
@@ -6717,7 +6876,16 @@ class RedemptionService {
|
|
|
6717
6876
|
/**
|
|
6718
6877
|
* UNIFIED: Get redemptions with intelligent access control and pagination
|
|
6719
6878
|
* @param options.active - Filter by active status
|
|
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
|
|
6720
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'
|
|
6885
|
+
* @param options.page - Page number (1-indexed)
|
|
6886
|
+
* @param options.limit - Results per page
|
|
6887
|
+
* @param options.sortBy - Field to sort by ('name' | 'createdAt' | 'startDate')
|
|
6888
|
+
* @param options.sortOrder - Sort direction ('ASC' | 'DESC')
|
|
6721
6889
|
*/
|
|
6722
6890
|
async getRedemptions(options) {
|
|
6723
6891
|
return this.redemptionApi.getRedemptions(options);
|
|
@@ -6751,7 +6919,7 @@ class RedemptionService {
|
|
|
6751
6919
|
}
|
|
6752
6920
|
/**
|
|
6753
6921
|
* UNIFIED: Get redemption redeems with filtering and pagination
|
|
6754
|
-
* @param filters - Filter options (redemptionId, userId, businessId, status, myRedeems)
|
|
6922
|
+
* @param filters - Filter options (redemptionId, userId, businessId, status, dateFrom, dateTo, myRedeems)
|
|
6755
6923
|
* @param include - Optional relations to include (redemption, user, business, transactions)
|
|
6756
6924
|
*/
|
|
6757
6925
|
async getRedemptionRedeems(filters, include) {
|
|
@@ -6770,10 +6938,11 @@ class RedemptionService {
|
|
|
6770
6938
|
// ADMIN OPERATIONS
|
|
6771
6939
|
// ==========================================
|
|
6772
6940
|
/**
|
|
6773
|
-
* Convenience: Get redemptions as admin
|
|
6941
|
+
* Convenience: Get redemptions as admin with filtering and search
|
|
6942
|
+
* @param options - Filter options (active, search, tags, pagination)
|
|
6774
6943
|
*/
|
|
6775
|
-
async getRedemptionsAsAdmin(
|
|
6776
|
-
return this.redemptionApi.getRedemptions({
|
|
6944
|
+
async getRedemptionsAsAdmin(options) {
|
|
6945
|
+
return this.redemptionApi.getRedemptions({ ...options, adminAccess: true });
|
|
6777
6946
|
}
|
|
6778
6947
|
/**
|
|
6779
6948
|
* ADMIN: Create redemption
|
|
@@ -7221,6 +7390,82 @@ class TransactionService {
|
|
|
7221
7390
|
return this.transactionApi.getTransactionAnalytics(analyticsRequest);
|
|
7222
7391
|
}
|
|
7223
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
|
+
}
|
|
7224
7469
|
// ============================================================================
|
|
7225
7470
|
// CLIENT TRANSACTION TYPES
|
|
7226
7471
|
// ============================================================================
|
|
@@ -7867,6 +8112,15 @@ class TenantApi {
|
|
|
7867
8112
|
// ==========================================
|
|
7868
8113
|
// ADMIN OPERATIONS
|
|
7869
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
|
+
}
|
|
7870
8124
|
/**
|
|
7871
8125
|
* ADMIN: Update tenant information
|
|
7872
8126
|
* UPDATED: Uses PUT /tenants (admin endpoint)
|
|
@@ -7941,6 +8195,12 @@ class TenantService {
|
|
|
7941
8195
|
// ==========================================
|
|
7942
8196
|
// ADMIN OPERATIONS
|
|
7943
8197
|
// ==========================================
|
|
8198
|
+
/**
|
|
8199
|
+
* ADMIN: Get current admin info (tenant admin authentication required)
|
|
8200
|
+
*/
|
|
8201
|
+
async getCurrentAdmin() {
|
|
8202
|
+
return this.tenantApi.getCurrentAdmin();
|
|
8203
|
+
}
|
|
7944
8204
|
/**
|
|
7945
8205
|
* ADMIN: Update tenant information
|
|
7946
8206
|
* Returns full TenantDTO including sensitive config
|
|
@@ -8079,6 +8339,24 @@ class TenantManager {
|
|
|
8079
8339
|
// ==========================================
|
|
8080
8340
|
// ADMIN OPERATIONS
|
|
8081
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
|
+
}
|
|
8082
8360
|
/**
|
|
8083
8361
|
* Admin: Update tenant data
|
|
8084
8362
|
*
|
|
@@ -8665,7 +8943,8 @@ const DEFAULT_PERS_CONFIG = {
|
|
|
8665
8943
|
retries: 3,
|
|
8666
8944
|
tokenRefreshMargin: 60, // Refresh tokens 60 seconds before expiry
|
|
8667
8945
|
backgroundRefreshThreshold: 30, // Use background refresh if >30s remaining
|
|
8668
|
-
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
|
|
8669
8948
|
};
|
|
8670
8949
|
/**
|
|
8671
8950
|
* Build the API root URL based on config
|
|
@@ -8693,6 +8972,9 @@ function buildApiRoot(environment = 'production', version = 'v2', customApiUrl)
|
|
|
8693
8972
|
* @internal
|
|
8694
8973
|
*/
|
|
8695
8974
|
function buildWalletEventsWsUrl(environment = 'production', customWsUrl) {
|
|
8975
|
+
if (customWsUrl) {
|
|
8976
|
+
return customWsUrl;
|
|
8977
|
+
}
|
|
8696
8978
|
const wsUrls = {
|
|
8697
8979
|
staging: 'wss://events-staging.pers.ninja',
|
|
8698
8980
|
production: 'wss://events.pers.ninja'
|
|
@@ -8729,11 +9011,28 @@ class AuthApi {
|
|
|
8729
9011
|
}
|
|
8730
9012
|
/**
|
|
8731
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
|
+
* ```
|
|
8732
9030
|
*/
|
|
8733
|
-
async loginTenantAdmin(jwt) {
|
|
9031
|
+
async loginTenantAdmin(jwt, options) {
|
|
8734
9032
|
const body = {
|
|
8735
9033
|
authToken: jwt,
|
|
8736
|
-
authType: exports.AccountOwnerType.TENANT
|
|
9034
|
+
authType: exports.AccountOwnerType.TENANT,
|
|
9035
|
+
context: options?.tenantId ? { tenantId: options.tenantId } : undefined
|
|
8737
9036
|
};
|
|
8738
9037
|
return this.apiClient.post(`${this.basePath}/token`, body, { bypassAuth: true });
|
|
8739
9038
|
}
|
|
@@ -8791,13 +9090,37 @@ class AuthApi {
|
|
|
8791
9090
|
async refreshAccessToken(refreshToken) {
|
|
8792
9091
|
return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken }, { bypassAuth: true, isRefreshRequest: true });
|
|
8793
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
|
+
}
|
|
8794
9117
|
}
|
|
8795
9118
|
|
|
8796
9119
|
/**
|
|
8797
9120
|
* Authentication status enumeration
|
|
8798
9121
|
* Represents the current state of the authentication system
|
|
8799
9122
|
*/
|
|
8800
|
-
|
|
9123
|
+
exports.AuthStatus = void 0;
|
|
8801
9124
|
(function (AuthStatus) {
|
|
8802
9125
|
/** User is authenticated with valid tokens */
|
|
8803
9126
|
AuthStatus["AUTHENTICATED"] = "authenticated";
|
|
@@ -8813,8 +9136,14 @@ var AuthStatus;
|
|
|
8813
9136
|
AuthStatus["SESSION_RESTORED"] = "session_restored";
|
|
8814
9137
|
/** Session restoration determined no valid session exists */
|
|
8815
9138
|
AuthStatus["SESSION_RESTORATION_FAILED"] = "session_restoration_failed";
|
|
8816
|
-
})(AuthStatus || (AuthStatus = {}));
|
|
9139
|
+
})(exports.AuthStatus || (exports.AuthStatus = {}));
|
|
8817
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
|
+
}
|
|
8818
9147
|
/**
|
|
8819
9148
|
* Platform-agnostic authentication service
|
|
8820
9149
|
* Handles login, token refresh, and storage operations
|
|
@@ -8824,7 +9153,13 @@ class AuthService {
|
|
|
8824
9153
|
this.authApi = authApi;
|
|
8825
9154
|
this.authProvider = authProvider;
|
|
8826
9155
|
this.activeRefreshPromise = null;
|
|
8827
|
-
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;
|
|
8828
9163
|
}
|
|
8829
9164
|
/**
|
|
8830
9165
|
* Emit auth status change to the app
|
|
@@ -8833,9 +9168,10 @@ class AuthService {
|
|
|
8833
9168
|
if (this.currentAuthStatus === status)
|
|
8834
9169
|
return; // No change
|
|
8835
9170
|
this.currentAuthStatus = status;
|
|
8836
|
-
|
|
9171
|
+
const extended = this.extendedProvider;
|
|
9172
|
+
if (extended?.onAuthStatusChange) {
|
|
8837
9173
|
try {
|
|
8838
|
-
await
|
|
9174
|
+
await extended.onAuthStatusChange(status);
|
|
8839
9175
|
}
|
|
8840
9176
|
catch (error) {
|
|
8841
9177
|
console.warn('[AuthService] Auth status change callback failed:', error);
|
|
@@ -8847,9 +9183,14 @@ class AuthService {
|
|
|
8847
9183
|
// ==========================================
|
|
8848
9184
|
/**
|
|
8849
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
|
|
8850
9191
|
*/
|
|
8851
|
-
async loginTenantAdmin(jwt) {
|
|
8852
|
-
const response = await this.authApi.loginTenantAdmin(jwt);
|
|
9192
|
+
async loginTenantAdmin(jwt, options) {
|
|
9193
|
+
const response = await this.authApi.loginTenantAdmin(jwt, options);
|
|
8853
9194
|
if (this.authProvider && response.accessToken) {
|
|
8854
9195
|
await this.storeTokens(response.accessToken, response.refreshToken, exports.AccountOwnerType.TENANT, jwt);
|
|
8855
9196
|
}
|
|
@@ -8907,7 +9248,7 @@ class AuthService {
|
|
|
8907
9248
|
* Refresh access token with race condition protection
|
|
8908
9249
|
*/
|
|
8909
9250
|
async refreshAccessToken(refreshToken) {
|
|
8910
|
-
if (this.currentAuthStatus === AuthStatus.AUTH_FAILED) {
|
|
9251
|
+
if (this.currentAuthStatus === exports.AuthStatus.AUTH_FAILED) {
|
|
8911
9252
|
throw new Error('Auth failure has occurred, cannot refresh token');
|
|
8912
9253
|
}
|
|
8913
9254
|
if (this.activeRefreshPromise) {
|
|
@@ -8930,9 +9271,13 @@ class AuthService {
|
|
|
8930
9271
|
if (!tokenToUse) {
|
|
8931
9272
|
throw new Error('No refresh token available for token refresh');
|
|
8932
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;
|
|
8933
9277
|
const response = await this.authApi.refreshAccessToken(tokenToUse);
|
|
8934
9278
|
if (this.authProvider && response.accessToken) {
|
|
8935
|
-
|
|
9279
|
+
// Preserve the authType during refresh
|
|
9280
|
+
await this.storeTokens(response.accessToken, response.refreshToken, currentAuthType);
|
|
8936
9281
|
}
|
|
8937
9282
|
return response;
|
|
8938
9283
|
}
|
|
@@ -8950,7 +9295,7 @@ class AuthService {
|
|
|
8950
9295
|
* Centralized method to avoid redundant token cleanup logic
|
|
8951
9296
|
*/
|
|
8952
9297
|
async handleAuthFailure() {
|
|
8953
|
-
if (this.currentAuthStatus === AuthStatus.AUTH_FAILED) {
|
|
9298
|
+
if (this.currentAuthStatus === exports.AuthStatus.AUTH_FAILED) {
|
|
8954
9299
|
return;
|
|
8955
9300
|
}
|
|
8956
9301
|
// Clear active refresh operations to prevent hanging promises
|
|
@@ -8958,7 +9303,7 @@ class AuthService {
|
|
|
8958
9303
|
// Clear all stored tokens
|
|
8959
9304
|
await this.clearTokens();
|
|
8960
9305
|
// Emit auth failure status to notify the app
|
|
8961
|
-
await this.emitAuthStatus(AuthStatus.AUTH_FAILED);
|
|
9306
|
+
await this.emitAuthStatus(exports.AuthStatus.AUTH_FAILED);
|
|
8962
9307
|
}
|
|
8963
9308
|
/**
|
|
8964
9309
|
* Check if we have valid authentication tokens
|
|
@@ -8970,6 +9315,27 @@ class AuthService {
|
|
|
8970
9315
|
return !!token;
|
|
8971
9316
|
}
|
|
8972
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
|
+
// ==========================================
|
|
8973
9339
|
// PRIVATE HELPERS
|
|
8974
9340
|
// ==========================================
|
|
8975
9341
|
/**
|
|
@@ -8983,20 +9349,21 @@ class AuthService {
|
|
|
8983
9349
|
if (refreshToken) {
|
|
8984
9350
|
await this.authProvider.setRefreshToken(refreshToken);
|
|
8985
9351
|
}
|
|
8986
|
-
//
|
|
8987
|
-
const
|
|
8988
|
-
if (
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
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
|
+
}
|
|
8994
9361
|
}
|
|
8995
|
-
else {
|
|
8996
|
-
console.warn('[AuthService] Auth type not stored -
|
|
9362
|
+
else if (authType) {
|
|
9363
|
+
console.warn('[AuthService] Auth type not stored - provider does not implement setAuthType');
|
|
8997
9364
|
}
|
|
8998
9365
|
// Emit authenticated status on successful token storage
|
|
8999
|
-
await this.emitAuthStatus(AuthStatus.AUTHENTICATED);
|
|
9366
|
+
await this.emitAuthStatus(exports.AuthStatus.AUTHENTICATED);
|
|
9000
9367
|
}
|
|
9001
9368
|
catch (error) {
|
|
9002
9369
|
console.warn('[AuthService] Failed to store tokens:', error);
|
|
@@ -9487,7 +9854,7 @@ class DefaultAuthProvider {
|
|
|
9487
9854
|
async onAuthStatusChange(status) {
|
|
9488
9855
|
try {
|
|
9489
9856
|
// Handle provider-level responsibilities first
|
|
9490
|
-
if (status === AuthStatus.TOKEN_EXPIRED && this.config.tokenProvider) {
|
|
9857
|
+
if (status === exports.AuthStatus.TOKEN_EXPIRED && this.config.tokenProvider) {
|
|
9491
9858
|
const newToken = await this.config.tokenProvider();
|
|
9492
9859
|
if (newToken) {
|
|
9493
9860
|
await this.tokenManager.setAccessToken(newToken);
|
|
@@ -9912,6 +10279,108 @@ class PersApiClient {
|
|
|
9912
10279
|
}
|
|
9913
10280
|
}
|
|
9914
10281
|
|
|
10282
|
+
/**
|
|
10283
|
+
* Static JWT Authentication Provider
|
|
10284
|
+
* For server-side integrations with long-lived JWT tokens
|
|
10285
|
+
*
|
|
10286
|
+
* Use this provider when:
|
|
10287
|
+
* - Running in server environments (Node.js, serverless functions)
|
|
10288
|
+
* - Using static/long-lived JWT tokens (not OAuth flow)
|
|
10289
|
+
* - No token refresh mechanism needed
|
|
10290
|
+
* - JWT rotation handled externally (e.g., environment variables)
|
|
10291
|
+
*
|
|
10292
|
+
* @example Server-side usage
|
|
10293
|
+
* ```typescript
|
|
10294
|
+
* const sdk = new PersSdk(httpClient, {
|
|
10295
|
+
* environment: 'production',
|
|
10296
|
+
* apiProjectKey: 'your-project',
|
|
10297
|
+
* authProvider: new StaticJwtAuthProvider(
|
|
10298
|
+
* process.env.PERS_JWT_TOKEN!,
|
|
10299
|
+
* 'your-project',
|
|
10300
|
+
* 'business' // or 'admin'
|
|
10301
|
+
* )
|
|
10302
|
+
* });
|
|
10303
|
+
* ```
|
|
10304
|
+
*/
|
|
10305
|
+
/**
|
|
10306
|
+
* Simple authentication provider for static JWT tokens
|
|
10307
|
+
* No token storage, refresh, or DPoP - designed for server-side use
|
|
10308
|
+
*/
|
|
10309
|
+
class StaticJwtAuthProvider {
|
|
10310
|
+
constructor(configOrJwt, projectKey, authType) {
|
|
10311
|
+
if (typeof configOrJwt === 'string') {
|
|
10312
|
+
// Legacy constructor: (jwt, projectKey, authType)
|
|
10313
|
+
this.jwt = configOrJwt;
|
|
10314
|
+
this.projectKey = projectKey;
|
|
10315
|
+
this.authType = authType || exports.AccountOwnerType.BUSINESS;
|
|
10316
|
+
}
|
|
10317
|
+
else {
|
|
10318
|
+
// New constructor: (config)
|
|
10319
|
+
this.jwt = configOrJwt.jwt;
|
|
10320
|
+
this.projectKey = configOrJwt.projectKey;
|
|
10321
|
+
this.authType = configOrJwt.authType || exports.AccountOwnerType.BUSINESS;
|
|
10322
|
+
this.statusChangeCallback = configOrJwt.onAuthStatusChange;
|
|
10323
|
+
}
|
|
10324
|
+
if (!this.jwt) {
|
|
10325
|
+
throw new Error('StaticJwtAuthProvider: JWT token is required');
|
|
10326
|
+
}
|
|
10327
|
+
if (!this.projectKey) {
|
|
10328
|
+
throw new Error('StaticJwtAuthProvider: Project key is required');
|
|
10329
|
+
}
|
|
10330
|
+
}
|
|
10331
|
+
/**
|
|
10332
|
+
* Get the static JWT token
|
|
10333
|
+
* @returns The configured JWT token
|
|
10334
|
+
*/
|
|
10335
|
+
async getToken() {
|
|
10336
|
+
return this.jwt;
|
|
10337
|
+
}
|
|
10338
|
+
/**
|
|
10339
|
+
* Get project key for API requests
|
|
10340
|
+
* @returns The configured project key
|
|
10341
|
+
*/
|
|
10342
|
+
async getProjectKey() {
|
|
10343
|
+
return this.projectKey;
|
|
10344
|
+
}
|
|
10345
|
+
/**
|
|
10346
|
+
* Get authentication headers (Bearer token)
|
|
10347
|
+
* Note: No DPoP support for server-side static tokens
|
|
10348
|
+
*
|
|
10349
|
+
* @param token - The access token (ignored, uses configured JWT)
|
|
10350
|
+
* @returns Headers with Bearer authorization
|
|
10351
|
+
*/
|
|
10352
|
+
async getAuthHeaders(token) {
|
|
10353
|
+
const actualToken = token || this.jwt;
|
|
10354
|
+
return actualToken ? { 'Authorization': `Bearer ${actualToken}` } : {};
|
|
10355
|
+
}
|
|
10356
|
+
/**
|
|
10357
|
+
* Handle authentication status changes
|
|
10358
|
+
*
|
|
10359
|
+
* Note: Static JWTs cannot be refreshed automatically.
|
|
10360
|
+
* TOKEN_EXPIRED status indicates manual JWT rotation is needed.
|
|
10361
|
+
*
|
|
10362
|
+
* @param status - The new authentication status
|
|
10363
|
+
*/
|
|
10364
|
+
async onAuthStatusChange(status) {
|
|
10365
|
+
if (status === exports.AuthStatus.TOKEN_EXPIRED) {
|
|
10366
|
+
console.warn('[StaticJwtAuthProvider] JWT token expired. Manual rotation required. ' +
|
|
10367
|
+
'Update the JWT in your environment configuration.');
|
|
10368
|
+
}
|
|
10369
|
+
// Delegate to custom callback if provided
|
|
10370
|
+
if (this.statusChangeCallback) {
|
|
10371
|
+
await this.statusChangeCallback(status);
|
|
10372
|
+
}
|
|
10373
|
+
}
|
|
10374
|
+
/**
|
|
10375
|
+
* Clear tokens (no-op for static provider)
|
|
10376
|
+
* Static JWTs are immutable and managed externally
|
|
10377
|
+
*/
|
|
10378
|
+
async clearTokens() {
|
|
10379
|
+
// No-op: Static JWTs cannot be cleared, they must be rotated externally
|
|
10380
|
+
console.warn('[StaticJwtAuthProvider] clearTokens() called but static JWTs cannot be cleared. Rotate JWT externally.');
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
|
|
9915
10384
|
/**
|
|
9916
10385
|
* PERS SDK Event Emitter
|
|
9917
10386
|
*
|
|
@@ -9931,6 +10400,7 @@ function generateEventId() {
|
|
|
9931
10400
|
*
|
|
9932
10401
|
* All events flow through one subscription point.
|
|
9933
10402
|
* Optional filtering at subscription level.
|
|
10403
|
+
* Supports replay of last auth event for late subscribers.
|
|
9934
10404
|
*
|
|
9935
10405
|
* @example Basic Usage
|
|
9936
10406
|
* ```typescript
|
|
@@ -9954,6 +10424,13 @@ function generateEventId() {
|
|
|
9954
10424
|
* showNotification(event.userMessage);
|
|
9955
10425
|
* }, { excludeDomains: ['validation'] });
|
|
9956
10426
|
*
|
|
10427
|
+
* // Subscribe with replay - get last auth event if missed
|
|
10428
|
+
* sdk.events.subscribe((event) => {
|
|
10429
|
+
* if (event.type === 'session_restored') {
|
|
10430
|
+
* setUser(event.details.user);
|
|
10431
|
+
* }
|
|
10432
|
+
* }, { domains: ['authentication'], replay: true });
|
|
10433
|
+
*
|
|
9957
10434
|
* // Cleanup
|
|
9958
10435
|
* unsubscribe();
|
|
9959
10436
|
* ```
|
|
@@ -9961,6 +10438,8 @@ function generateEventId() {
|
|
|
9961
10438
|
let emitterInstanceCounter = 0;
|
|
9962
10439
|
class PersEventEmitter {
|
|
9963
10440
|
constructor() {
|
|
10441
|
+
// Store last auth event for replay to late subscribers
|
|
10442
|
+
this.lastAuthEvent = null;
|
|
9964
10443
|
this.handlers = new Set();
|
|
9965
10444
|
this._instanceId = ++emitterInstanceCounter;
|
|
9966
10445
|
}
|
|
@@ -9971,7 +10450,7 @@ class PersEventEmitter {
|
|
|
9971
10450
|
* Subscribe to events
|
|
9972
10451
|
*
|
|
9973
10452
|
* @param handler - Callback for matching events
|
|
9974
|
-
* @param filter - Optional filter by domain and/or level
|
|
10453
|
+
* @param filter - Optional filter by domain and/or level. Set `replay: true` to receive last auth event if missed.
|
|
9975
10454
|
* @returns Unsubscribe function
|
|
9976
10455
|
*
|
|
9977
10456
|
* @example All events
|
|
@@ -9995,10 +10474,35 @@ class PersEventEmitter {
|
|
|
9995
10474
|
* confetti();
|
|
9996
10475
|
* }, { domains: ['transaction'], levels: ['success'] });
|
|
9997
10476
|
* ```
|
|
10477
|
+
*
|
|
10478
|
+
* @example Auth events with replay (catches session_restored even if emitted before subscription)
|
|
10479
|
+
* ```typescript
|
|
10480
|
+
* sdk.events.subscribe((event) => {
|
|
10481
|
+
* if (event.type === 'session_restored') {
|
|
10482
|
+
* const { user } = event.details;
|
|
10483
|
+
* setUser(user);
|
|
10484
|
+
* }
|
|
10485
|
+
* }, { domains: ['authentication'], replay: true });
|
|
10486
|
+
* ```
|
|
9998
10487
|
*/
|
|
9999
10488
|
subscribe(handler, filter) {
|
|
10000
10489
|
const filteredHandler = { handler, filter };
|
|
10001
10490
|
this.handlers.add(filteredHandler);
|
|
10491
|
+
// If replay is requested and we have a cached auth event, replay it immediately
|
|
10492
|
+
if (filter?.replay && this.lastAuthEvent) {
|
|
10493
|
+
// Check if event matches the filter
|
|
10494
|
+
if (!filter || this.matchesFilter(this.lastAuthEvent, filter)) {
|
|
10495
|
+
// Use queueMicrotask to ensure handler runs after subscribe returns
|
|
10496
|
+
queueMicrotask(() => {
|
|
10497
|
+
try {
|
|
10498
|
+
handler(this.lastAuthEvent);
|
|
10499
|
+
}
|
|
10500
|
+
catch (error) {
|
|
10501
|
+
console.error('[PersEventEmitter] Replay handler error:', error);
|
|
10502
|
+
}
|
|
10503
|
+
});
|
|
10504
|
+
}
|
|
10505
|
+
}
|
|
10002
10506
|
return () => this.handlers.delete(filteredHandler);
|
|
10003
10507
|
}
|
|
10004
10508
|
/**
|
|
@@ -10023,6 +10527,7 @@ class PersEventEmitter {
|
|
|
10023
10527
|
* Emit a success event
|
|
10024
10528
|
*
|
|
10025
10529
|
* Domain is restricted to business domains only (Domain type).
|
|
10530
|
+
* Auth events (login_success, session_restored) are cached for replay to late subscribers.
|
|
10026
10531
|
*
|
|
10027
10532
|
* @param event - Success event data (id and timestamp auto-generated)
|
|
10028
10533
|
* @returns The complete event object
|
|
@@ -10043,6 +10548,15 @@ class PersEventEmitter {
|
|
|
10043
10548
|
id: generateEventId(),
|
|
10044
10549
|
timestamp: Date.now()
|
|
10045
10550
|
};
|
|
10551
|
+
// Cache auth events for replay to late subscribers
|
|
10552
|
+
if (event.domain === 'authentication' &&
|
|
10553
|
+
(event.type === 'login_success' || event.type === 'session_restored')) {
|
|
10554
|
+
this.lastAuthEvent = fullEvent;
|
|
10555
|
+
}
|
|
10556
|
+
// Clear cached auth on logout
|
|
10557
|
+
if (event.domain === 'authentication' && event.type === 'logout_success') {
|
|
10558
|
+
this.lastAuthEvent = null;
|
|
10559
|
+
}
|
|
10046
10560
|
this.notifyHandlers(fullEvent);
|
|
10047
10561
|
return fullEvent;
|
|
10048
10562
|
}
|
|
@@ -10070,6 +10584,10 @@ class PersEventEmitter {
|
|
|
10070
10584
|
id: generateEventId(),
|
|
10071
10585
|
timestamp: Date.now()
|
|
10072
10586
|
};
|
|
10587
|
+
// Clear cached auth on auth failure (session invalid)
|
|
10588
|
+
if (event.domain === 'authentication' && event.type === 'session_restoration_failed') {
|
|
10589
|
+
this.lastAuthEvent = null;
|
|
10590
|
+
}
|
|
10073
10591
|
this.notifyHandlers(fullEvent);
|
|
10074
10592
|
return fullEvent;
|
|
10075
10593
|
}
|
|
@@ -10193,7 +10711,8 @@ class AuthManager {
|
|
|
10193
10711
|
this.events?.emitSuccess({
|
|
10194
10712
|
domain: 'authentication',
|
|
10195
10713
|
type: 'login_success',
|
|
10196
|
-
userMessage: 'Successfully logged in'
|
|
10714
|
+
userMessage: 'Successfully logged in',
|
|
10715
|
+
details: { user: result.user }
|
|
10197
10716
|
});
|
|
10198
10717
|
return result;
|
|
10199
10718
|
}
|
|
@@ -10241,7 +10760,66 @@ class AuthManager {
|
|
|
10241
10760
|
*/
|
|
10242
10761
|
async loginAsBusiness(jwtToken, options) {
|
|
10243
10762
|
const authService = this.apiClient.getAuthService();
|
|
10244
|
-
|
|
10763
|
+
const result = await authService.loginBusiness(jwtToken, options);
|
|
10764
|
+
this.events?.emitSuccess({
|
|
10765
|
+
domain: 'authentication',
|
|
10766
|
+
type: 'login_success',
|
|
10767
|
+
userMessage: 'Successfully logged in as business',
|
|
10768
|
+
details: { user: result.user, business: result.business }
|
|
10769
|
+
});
|
|
10770
|
+
return result;
|
|
10771
|
+
}
|
|
10772
|
+
/**
|
|
10773
|
+
* Login as tenant admin with JWT token
|
|
10774
|
+
*
|
|
10775
|
+
* Authenticates an admin in a tenant context.
|
|
10776
|
+
*
|
|
10777
|
+
* **Auto-Selection Behavior:**
|
|
10778
|
+
* - If admin has access to a single tenant, it's auto-selected
|
|
10779
|
+
* - If admin has access to multiple tenants and no tenantId is provided,
|
|
10780
|
+
* throws `MULTIPLE_CONTEXT_SELECTION_REQUIRED` error with available options
|
|
10781
|
+
*
|
|
10782
|
+
* @param jwtToken - JWT token from auth provider (Firebase, etc.)
|
|
10783
|
+
* @param options - Tenant authentication options (tenantId for multi-tenant admins)
|
|
10784
|
+
* @returns Promise resolving to authentication response with tenant context
|
|
10785
|
+
* @throws Error with code `MULTIPLE_CONTEXT_SELECTION_REQUIRED` when tenantId is needed
|
|
10786
|
+
*
|
|
10787
|
+
* @example Single Tenant Access
|
|
10788
|
+
* ```typescript
|
|
10789
|
+
* // Auto-selects the admin's only tenant
|
|
10790
|
+
* const result = await sdk.auth.loginAsTenant(jwt);
|
|
10791
|
+
* console.log('Tenant:', result.admin?.tenantId);
|
|
10792
|
+
* ```
|
|
10793
|
+
*
|
|
10794
|
+
* @example Multiple Tenant Access
|
|
10795
|
+
* ```typescript
|
|
10796
|
+
* try {
|
|
10797
|
+
* const result = await sdk.auth.loginAsTenant(jwt);
|
|
10798
|
+
* } catch (error) {
|
|
10799
|
+
* if (error.code === 'MULTIPLE_CONTEXT_SELECTION_REQUIRED') {
|
|
10800
|
+
* // Show tenant selector UI
|
|
10801
|
+
* const selectedId = await showTenantSelector(error.details.availableOptions);
|
|
10802
|
+
* const result = await sdk.auth.loginAsTenant(jwt, { tenantId: selectedId });
|
|
10803
|
+
* }
|
|
10804
|
+
* }
|
|
10805
|
+
* ```
|
|
10806
|
+
*
|
|
10807
|
+
* @example Direct Tenant Selection
|
|
10808
|
+
* ```typescript
|
|
10809
|
+
* const result = await sdk.auth.loginAsTenant(jwt, { tenantId: 'tenant-123' });
|
|
10810
|
+
* console.log('Authenticated as admin for tenant:', result.admin?.tenantId);
|
|
10811
|
+
* ```
|
|
10812
|
+
*/
|
|
10813
|
+
async loginAsTenant(jwtToken, options) {
|
|
10814
|
+
const authService = this.apiClient.getAuthService();
|
|
10815
|
+
const result = await authService.loginTenantAdmin(jwtToken, options);
|
|
10816
|
+
this.events?.emitSuccess({
|
|
10817
|
+
domain: 'authentication',
|
|
10818
|
+
type: 'login_success',
|
|
10819
|
+
userMessage: 'Successfully logged in as tenant admin',
|
|
10820
|
+
details: { admin: result.admin }
|
|
10821
|
+
});
|
|
10822
|
+
return result;
|
|
10245
10823
|
}
|
|
10246
10824
|
/**
|
|
10247
10825
|
* Get current business context
|
|
@@ -10259,7 +10837,27 @@ class AuthManager {
|
|
|
10259
10837
|
* ```
|
|
10260
10838
|
*/
|
|
10261
10839
|
async getCurrentBusiness() {
|
|
10262
|
-
|
|
10840
|
+
const authService = this.apiClient.getAuthService();
|
|
10841
|
+
return authService.getCurrentBusiness();
|
|
10842
|
+
}
|
|
10843
|
+
/**
|
|
10844
|
+
* Get current admin context
|
|
10845
|
+
*
|
|
10846
|
+
* Retrieves the current admin data if authenticated as tenant admin.
|
|
10847
|
+
* Requires prior tenant authentication via {@link loginAsTenant}.
|
|
10848
|
+
*
|
|
10849
|
+
* @returns Promise resolving to current admin data
|
|
10850
|
+
* @throws {PersApiError} When not authenticated as tenant admin
|
|
10851
|
+
*
|
|
10852
|
+
* @example
|
|
10853
|
+
* ```typescript
|
|
10854
|
+
* const admin = await sdk.auth.getCurrentAdmin();
|
|
10855
|
+
* console.log('Current admin:', admin.email);
|
|
10856
|
+
* ```
|
|
10857
|
+
*/
|
|
10858
|
+
async getCurrentAdmin() {
|
|
10859
|
+
const authService = this.apiClient.getAuthService();
|
|
10860
|
+
return authService.getCurrentAdmin();
|
|
10263
10861
|
}
|
|
10264
10862
|
/**
|
|
10265
10863
|
* Login with raw user data
|
|
@@ -10303,7 +10901,8 @@ class AuthManager {
|
|
|
10303
10901
|
* ```
|
|
10304
10902
|
*/
|
|
10305
10903
|
async getCurrentUser() {
|
|
10306
|
-
|
|
10904
|
+
const authService = this.apiClient.getAuthService();
|
|
10905
|
+
return authService.getCurrentUser();
|
|
10307
10906
|
}
|
|
10308
10907
|
/**
|
|
10309
10908
|
* Check if user is authenticated
|
|
@@ -11890,7 +12489,7 @@ class BusinessManager {
|
|
|
11890
12489
|
*
|
|
11891
12490
|
* // Get specific campaign details
|
|
11892
12491
|
* const campaign = await sdk.campaigns.getCampaignById('summer-promo-2024');
|
|
11893
|
-
* console.log('Campaign:', campaign.
|
|
12492
|
+
* console.log('Campaign:', campaign.name);
|
|
11894
12493
|
*
|
|
11895
12494
|
* // User claims campaign reward
|
|
11896
12495
|
* const claim = await sdk.campaigns.claimCampaign({
|
|
@@ -11906,7 +12505,7 @@ class BusinessManager {
|
|
|
11906
12505
|
*
|
|
11907
12506
|
* console.log('My Campaign Claims:');
|
|
11908
12507
|
* userClaims.forEach(claim => {
|
|
11909
|
-
* console.log(`- ${claim.campaign.
|
|
12508
|
+
* console.log(`- ${claim.campaign.name}`);
|
|
11910
12509
|
* console.log(` Rewards: ${claim.rewards?.length || 0} tokens`);
|
|
11911
12510
|
* console.log(` Date: ${claim.claimedAt}`);
|
|
11912
12511
|
* });
|
|
@@ -11922,7 +12521,7 @@ class BusinessManager {
|
|
|
11922
12521
|
* ```typescript
|
|
11923
12522
|
* // Admin: Create seasonal campaign
|
|
11924
12523
|
* const newCampaign = await sdk.campaigns.createCampaign({
|
|
11925
|
-
*
|
|
12524
|
+
* name: 'Holiday Rewards',
|
|
11926
12525
|
* description: 'Special holiday loyalty bonuses',
|
|
11927
12526
|
* startDate: '2024-12-01',
|
|
11928
12527
|
* endDate: '2024-12-31',
|
|
@@ -11961,7 +12560,7 @@ class CampaignManager {
|
|
|
11961
12560
|
* const campaign = await sdk.campaigns.getCampaignById('summer-promo-2024');
|
|
11962
12561
|
*
|
|
11963
12562
|
* console.log('Campaign Details:');
|
|
11964
|
-
* console.log('
|
|
12563
|
+
* console.log('Name:', campaign.name);
|
|
11965
12564
|
* console.log('Description:', campaign.description);
|
|
11966
12565
|
* console.log('Active:', campaign.isActive);
|
|
11967
12566
|
* console.log('Period:', `${campaign.startDate} to ${campaign.endDate}`);
|
|
@@ -12075,7 +12674,7 @@ class CampaignManager {
|
|
|
12075
12674
|
* console.log(`Campaign History (${userClaims.length} claims):`);
|
|
12076
12675
|
*
|
|
12077
12676
|
* userClaims.forEach((claim, index) => {
|
|
12078
|
-
* console.log(`\n${index + 1}. ${claim.campaign.
|
|
12677
|
+
* console.log(`\n${index + 1}. ${claim.campaign.name}`);
|
|
12079
12678
|
* console.log(` Claimed: ${new Date(claim.claimedAt).toLocaleDateString()}`);
|
|
12080
12679
|
* console.log(` Business: ${claim.business?.displayName || 'N/A'}`);
|
|
12081
12680
|
*
|
|
@@ -12105,7 +12704,7 @@ class CampaignManager {
|
|
|
12105
12704
|
* Returns campaigns with pagination metadata for efficient data loading.
|
|
12106
12705
|
* Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all.
|
|
12107
12706
|
*
|
|
12108
|
-
* @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, businessId, include)
|
|
12707
|
+
* @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, tag, search, businessId, startDate, endDate, include)
|
|
12109
12708
|
* @returns Promise resolving to paginated campaigns with metadata
|
|
12110
12709
|
*
|
|
12111
12710
|
* @example
|
|
@@ -12122,18 +12721,35 @@ class CampaignManager {
|
|
|
12122
12721
|
* limit: 20
|
|
12123
12722
|
* });
|
|
12124
12723
|
*
|
|
12125
|
-
* //
|
|
12126
|
-
* const
|
|
12127
|
-
*
|
|
12128
|
-
* sortBy: 'createdAt',
|
|
12129
|
-
* sortOrder: 'DESC',
|
|
12724
|
+
* // Search campaigns by name or description
|
|
12725
|
+
* const searchResults = await sdk.campaigns.getCampaigns({
|
|
12726
|
+
* search: 'summer rewards',
|
|
12130
12727
|
* page: 1,
|
|
12131
|
-
* limit:
|
|
12728
|
+
* limit: 20
|
|
12132
12729
|
* });
|
|
12133
12730
|
*
|
|
12134
|
-
* //
|
|
12731
|
+
* // Filter by tag
|
|
12732
|
+
* const taggedCampaigns = await sdk.campaigns.getCampaigns({
|
|
12733
|
+
* tag: 'seasonal',
|
|
12734
|
+
* active: true
|
|
12735
|
+
* });
|
|
12736
|
+
*
|
|
12737
|
+
* // Filter by date range
|
|
12738
|
+
* const dateFiltered = await sdk.campaigns.getCampaigns({
|
|
12739
|
+
* startDate: new Date('2026-05-01'),
|
|
12740
|
+
* endDate: new Date('2026-12-31'),
|
|
12741
|
+
* active: true
|
|
12742
|
+
* });
|
|
12743
|
+
*
|
|
12744
|
+
* // Get campaigns with claim count
|
|
12745
|
+
* const withCounts = await sdk.campaigns.getCampaigns({
|
|
12746
|
+
* include: ['claimCount']
|
|
12747
|
+
* });
|
|
12748
|
+
* withCounts.data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
|
|
12749
|
+
*
|
|
12750
|
+
* // Include related data (trigger sources, businesses, and claim count)
|
|
12135
12751
|
* const campaignsWithRelations = await sdk.campaigns.getCampaigns({
|
|
12136
|
-
* include: ['triggerSources', 'businesses']
|
|
12752
|
+
* include: ['triggerSources', 'businesses', 'claimCount']
|
|
12137
12753
|
* });
|
|
12138
12754
|
* ```
|
|
12139
12755
|
*/
|
|
@@ -12155,7 +12771,7 @@ class CampaignManager {
|
|
|
12155
12771
|
* ```typescript
|
|
12156
12772
|
* // Admin operation - create seasonal campaign
|
|
12157
12773
|
* const campaign = await sdk.campaigns.createCampaign({
|
|
12158
|
-
*
|
|
12774
|
+
* name: 'Summer Travel Rewards',
|
|
12159
12775
|
* description: 'Earn bonus points for summer bookings',
|
|
12160
12776
|
* startDate: '2024-06-01T00:00:00Z',
|
|
12161
12777
|
* endDate: '2024-08-31T23:59:59Z',
|
|
@@ -12164,7 +12780,7 @@ class CampaignManager {
|
|
|
12164
12780
|
* campaignType: 'SEASONAL'
|
|
12165
12781
|
* });
|
|
12166
12782
|
*
|
|
12167
|
-
* console.log('Campaign created:', campaign.
|
|
12783
|
+
* console.log('Campaign created:', campaign.name);
|
|
12168
12784
|
* console.log('Campaign ID:', campaign.id);
|
|
12169
12785
|
*
|
|
12170
12786
|
* // Set up campaign rewards
|
|
@@ -12194,13 +12810,13 @@ class CampaignManager {
|
|
|
12194
12810
|
* ```typescript
|
|
12195
12811
|
* // Admin operation - extend campaign duration
|
|
12196
12812
|
* const updated = await sdk.campaigns.updateCampaign('summer-2024', {
|
|
12197
|
-
*
|
|
12813
|
+
* name: 'Extended Summer Travel Rewards',
|
|
12198
12814
|
* description: 'Earn bonus points for summer and early fall bookings',
|
|
12199
12815
|
* endDate: '2024-09-30T23:59:59Z', // Extended end date
|
|
12200
12816
|
* isActive: true
|
|
12201
12817
|
* });
|
|
12202
12818
|
*
|
|
12203
|
-
* console.log('Campaign updated:', updated.
|
|
12819
|
+
* console.log('Campaign updated:', updated.name);
|
|
12204
12820
|
* console.log('New end date:', updated.endDate);
|
|
12205
12821
|
* ```
|
|
12206
12822
|
*/
|
|
@@ -12584,58 +13200,52 @@ class CampaignManager {
|
|
|
12584
13200
|
async deleteCampaignBusinessEngagement(campaignId, engagementId) {
|
|
12585
13201
|
return this.campaignService.deleteCampaignBusinessEngagement(campaignId, engagementId);
|
|
12586
13202
|
}
|
|
12587
|
-
/**
|
|
12588
|
-
* Admin: Get all campaign claims
|
|
12589
|
-
*
|
|
12590
|
-
* Retrieves all campaign claims across the system for comprehensive reporting
|
|
12591
|
-
* and analytics. This operation requires administrator privileges and provides
|
|
12592
|
-
* system-wide visibility into campaign performance and user engagement.
|
|
12593
|
-
*
|
|
12594
|
-
* @returns Promise resolving to array of all campaign claims
|
|
12595
|
-
* @throws {PersApiError} When not authenticated as administrator
|
|
12596
|
-
*
|
|
12597
|
-
* @example
|
|
12598
|
-
* ```typescript
|
|
12599
|
-
* // Admin operation - get campaign performance data
|
|
12600
|
-
* const allClaims = await sdk.campaigns.getCampaignClaims();
|
|
12601
|
-
*
|
|
12602
|
-
* console.log(`Campaign Performance Report:`);
|
|
12603
|
-
* console.log(`Total claims: ${allClaims.length}`);
|
|
12604
|
-
*
|
|
12605
|
-
* // Analyze claims by campaign
|
|
12606
|
-
* const claimsByCampaign = allClaims.reduce((acc, claim) => {
|
|
12607
|
-
* const campaignId = claim.campaignId;
|
|
12608
|
-
* acc[campaignId] = (acc[campaignId] || 0) + 1;
|
|
12609
|
-
* return acc;
|
|
12610
|
-
* }, {});
|
|
12611
|
-
*
|
|
12612
|
-
* console.log('\nClaims by Campaign:');
|
|
12613
|
-
* Object.entries(claimsByCampaign).forEach(([campaignId, count]) => {
|
|
12614
|
-
* console.log(`${campaignId}: ${count} claims`);
|
|
12615
|
-
* });
|
|
12616
|
-
* ```
|
|
12617
|
-
*/
|
|
12618
13203
|
/**
|
|
12619
13204
|
* Admin: Get campaign claims with optional filters
|
|
12620
13205
|
*
|
|
12621
|
-
* Retrieves campaign claims with optional filtering by campaign, user, or
|
|
13206
|
+
* Retrieves campaign claims with optional filtering by campaign, user, business, or date range.
|
|
12622
13207
|
* This operation requires administrator privileges and provides system-wide
|
|
12623
13208
|
* visibility into campaign performance and user engagement.
|
|
12624
13209
|
*
|
|
12625
13210
|
* @param filters - Optional filters and pagination options
|
|
13211
|
+
* @param filters.campaignId - Filter by specific campaign ID
|
|
13212
|
+
* @param filters.userId - Filter by specific user ID
|
|
13213
|
+
* @param filters.businessId - Filter by specific business ID
|
|
13214
|
+
* @param filters.dateFrom - Filter claims from this date (inclusive)
|
|
13215
|
+
* @param filters.dateTo - Filter claims until this date (inclusive)
|
|
13216
|
+
* @param filters.page - Page number for pagination
|
|
13217
|
+
* @param filters.limit - Items per page
|
|
13218
|
+
* @param filters.sortBy - Field to sort by
|
|
13219
|
+
* @param filters.sortOrder - Sort direction ('asc' or 'desc')
|
|
13220
|
+
* @param include - Optional relations to include for enrichment
|
|
12626
13221
|
* @returns Promise resolving to paginated campaign claims
|
|
12627
13222
|
* @throws {PersApiError} When not authenticated as administrator
|
|
12628
13223
|
*
|
|
12629
|
-
* @example
|
|
13224
|
+
* @example Get All Claims
|
|
12630
13225
|
* ```typescript
|
|
12631
|
-
*
|
|
12632
|
-
*
|
|
13226
|
+
* const { data: allClaims, pagination } = await sdk.campaigns.getCampaignClaims();
|
|
13227
|
+
* console.log(`Total: ${pagination.total}`);
|
|
13228
|
+
* ```
|
|
13229
|
+
*
|
|
13230
|
+
* @example Filter by User
|
|
13231
|
+
* ```typescript
|
|
13232
|
+
* const { data: userClaims } = await sdk.campaigns.getCampaignClaims({ userId: 'user-123' });
|
|
13233
|
+
* ```
|
|
12633
13234
|
*
|
|
12634
|
-
*
|
|
12635
|
-
*
|
|
13235
|
+
* @example Filter by Date Range
|
|
13236
|
+
* ```typescript
|
|
13237
|
+
* // Get claims within a specific date range
|
|
13238
|
+
* const { data: monthClaims } = await sdk.campaigns.getCampaignClaims({
|
|
13239
|
+
* dateFrom: new Date('2024-01-01'),
|
|
13240
|
+
* dateTo: new Date('2024-01-31'),
|
|
13241
|
+
* limit: 100
|
|
13242
|
+
* });
|
|
13243
|
+
* console.log(`${monthClaims.length} claims in January 2024`);
|
|
13244
|
+
* ```
|
|
12636
13245
|
*
|
|
12637
|
-
*
|
|
12638
|
-
*
|
|
13246
|
+
* @example With Pagination and Campaign Filter
|
|
13247
|
+
* ```typescript
|
|
13248
|
+
* const { data: page1 } = await sdk.campaigns.getCampaignClaims({
|
|
12639
13249
|
* campaignId: 'campaign-456',
|
|
12640
13250
|
* page: 1,
|
|
12641
13251
|
* limit: 50
|
|
@@ -12665,7 +13275,7 @@ class CampaignManager {
|
|
|
12665
13275
|
* console.log(`Total campaigns claimed: ${userClaims.length}`);
|
|
12666
13276
|
*
|
|
12667
13277
|
* userClaims.forEach((claim, index) => {
|
|
12668
|
-
* console.log(`\n${index + 1}. ${claim.campaign.
|
|
13278
|
+
* console.log(`\n${index + 1}. ${claim.campaign.name}`);
|
|
12669
13279
|
* console.log(` Date: ${claim.claimedAt}`);
|
|
12670
13280
|
* console.log(` Business: ${claim.business?.displayName || 'Direct'}`);
|
|
12671
13281
|
* console.log(` Rewards: ${claim.rewards?.length || 0} items`);
|
|
@@ -12702,8 +13312,8 @@ class CampaignManager {
|
|
|
12702
13312
|
*
|
|
12703
13313
|
* // Analyze claims by campaign
|
|
12704
13314
|
* const claimsByCampaign = businessClaims.reduce((acc, claim) => {
|
|
12705
|
-
* const
|
|
12706
|
-
* acc[
|
|
13315
|
+
* const campaignName = claim.campaign.name;
|
|
13316
|
+
* acc[campaignName] = (acc[campaignName] || 0) + 1;
|
|
12707
13317
|
* return acc;
|
|
12708
13318
|
* }, {});
|
|
12709
13319
|
*
|
|
@@ -12907,6 +13517,7 @@ class RedemptionManager {
|
|
|
12907
13517
|
*
|
|
12908
13518
|
* @param options - Optional filters and pagination:
|
|
12909
13519
|
* - `active`: Filter by active status (true/false). Admins only - regular users always see active offers
|
|
13520
|
+
* - `search`: Free text search across name, description, tags (case-insensitive)
|
|
12910
13521
|
* - Pagination options: `page`, `limit`, `sortBy`, `sortOrder`
|
|
12911
13522
|
* @returns Promise resolving to paginated redemption offers
|
|
12912
13523
|
*
|
|
@@ -12919,9 +13530,9 @@ class RedemptionManager {
|
|
|
12919
13530
|
* console.log(`Showing ${offers.length} of ${pagination.total} offers`);
|
|
12920
13531
|
*
|
|
12921
13532
|
* offers.forEach(offer => {
|
|
12922
|
-
* console.log(`\n${offer.
|
|
13533
|
+
* console.log(`\n${offer.name}`);
|
|
12923
13534
|
* console.log(`${offer.description}`);
|
|
12924
|
-
* console.log(`Type: ${offer.
|
|
13535
|
+
* console.log(`Type: ${offer.type?.name || 'General'}`);
|
|
12925
13536
|
*
|
|
12926
13537
|
* if (offer.tokenUnits?.length) {
|
|
12927
13538
|
* console.log('Cost:');
|
|
@@ -12944,6 +13555,23 @@ class RedemptionManager {
|
|
|
12944
13555
|
* console.log(`\n${affordableOffers.length} offers within your budget`);
|
|
12945
13556
|
* ```
|
|
12946
13557
|
*
|
|
13558
|
+
* @example Search Redemptions
|
|
13559
|
+
* ```typescript
|
|
13560
|
+
* // Search by text (matches name, description, tags)
|
|
13561
|
+
* const { data: coffeeOffers } = await sdk.redemptions.getRedemptions({
|
|
13562
|
+
* search: 'coffee'
|
|
13563
|
+
* });
|
|
13564
|
+
*
|
|
13565
|
+
* console.log(`Found ${coffeeOffers.length} coffee-related offers`);
|
|
13566
|
+
*
|
|
13567
|
+
* // Search with sorting
|
|
13568
|
+
* const { data: sortedResults } = await sdk.redemptions.getRedemptions({
|
|
13569
|
+
* search: 'discount',
|
|
13570
|
+
* sortBy: 'name',
|
|
13571
|
+
* sortOrder: 'ASC'
|
|
13572
|
+
* });
|
|
13573
|
+
* ```
|
|
13574
|
+
*
|
|
12947
13575
|
* @example Admin - Manage All Redemptions
|
|
12948
13576
|
* ```typescript
|
|
12949
13577
|
* // Admins can see all redemptions and filter by status
|
|
@@ -12956,14 +13584,11 @@ class RedemptionManager {
|
|
|
12956
13584
|
* console.log(`Active offers: ${activeOnly.length}`);
|
|
12957
13585
|
* console.log(`Inactive offers: ${inactiveOnly.length}`);
|
|
12958
13586
|
*
|
|
12959
|
-
* //
|
|
12960
|
-
* const
|
|
12961
|
-
*
|
|
12962
|
-
*
|
|
12963
|
-
*
|
|
12964
|
-
* }, {});
|
|
12965
|
-
*
|
|
12966
|
-
* console.log('\nBy type:', offersByType);
|
|
13587
|
+
* // Search inactive redemptions
|
|
13588
|
+
* const { data: inactiveVouchers } = await sdk.redemptions.getRedemptions({
|
|
13589
|
+
* active: false,
|
|
13590
|
+
* search: 'voucher'
|
|
13591
|
+
* });
|
|
12967
13592
|
* ```
|
|
12968
13593
|
*
|
|
12969
13594
|
* @example Pagination
|
|
@@ -13163,9 +13788,9 @@ class RedemptionManager {
|
|
|
13163
13788
|
* console.log(`Redemption History (${userRedemptions.length} of ${pagination.total} items):`);
|
|
13164
13789
|
*
|
|
13165
13790
|
* userRedemptions.forEach((redemption, index) => {
|
|
13166
|
-
* console.log(`\n${index + 1}. ${redemption.redemption.
|
|
13791
|
+
* console.log(`\n${index + 1}. ${redemption.redemption.name}`);
|
|
13167
13792
|
* console.log(` Date: ${new Date(redemption.redeemedAt).toLocaleDateString()}`);
|
|
13168
|
-
* console.log(` Type: ${redemption.redemption.
|
|
13793
|
+
* console.log(` Type: ${redemption.redemption.type?.name || 'General'}`);
|
|
13169
13794
|
*
|
|
13170
13795
|
* if (redemption.voucherCode) {
|
|
13171
13796
|
* console.log(` Voucher: ${redemption.voucherCode}`);
|
|
@@ -13225,9 +13850,19 @@ class RedemptionManager {
|
|
|
13225
13850
|
*
|
|
13226
13851
|
* Retrieves all redemption redeems across the platform with filtering capabilities.
|
|
13227
13852
|
* This is an admin-level operation that allows monitoring and analytics of redemption
|
|
13228
|
-
* activity. Supports filtering by user, redemption offer, and enrichment of related entities.
|
|
13229
|
-
*
|
|
13230
|
-
* @param filters - Filter options (userId, redemptionId, pagination)
|
|
13853
|
+
* activity. Supports filtering by user, redemption offer, date range, and enrichment of related entities.
|
|
13854
|
+
*
|
|
13855
|
+
* @param filters - Filter options (userId, redemptionId, businessId, status, dateFrom, dateTo, pagination)
|
|
13856
|
+
* @param filters.userId - Filter by specific user ID
|
|
13857
|
+
* @param filters.redemptionId - Filter by specific redemption offer ID
|
|
13858
|
+
* @param filters.businessId - Filter by specific business ID
|
|
13859
|
+
* @param filters.status - Filter by redeem status
|
|
13860
|
+
* @param filters.dateFrom - Filter redeems from this date (inclusive)
|
|
13861
|
+
* @param filters.dateTo - Filter redeems until this date (inclusive)
|
|
13862
|
+
* @param filters.page - Page number for pagination
|
|
13863
|
+
* @param filters.limit - Items per page
|
|
13864
|
+
* @param filters.sortBy - Field to sort by
|
|
13865
|
+
* @param filters.sortOrder - Sort direction ('asc' or 'desc')
|
|
13231
13866
|
* @param include - Optional relations to include for enrichment
|
|
13232
13867
|
* @returns Promise resolving to paginated list of redemption redeems
|
|
13233
13868
|
* @throws {PersApiError} When not authenticated as admin
|
|
@@ -13252,6 +13887,18 @@ class RedemptionManager {
|
|
|
13252
13887
|
* console.log(`User has ${userRedeems.length} redemptions`);
|
|
13253
13888
|
* ```
|
|
13254
13889
|
*
|
|
13890
|
+
* @example Filter by Date Range
|
|
13891
|
+
* ```typescript
|
|
13892
|
+
* // Get redeems within a specific date range
|
|
13893
|
+
* const { data: recentRedeems } = await sdk.redemptions.getRedemptionRedeems({
|
|
13894
|
+
* dateFrom: new Date('2024-01-01'),
|
|
13895
|
+
* dateTo: new Date('2024-01-31'),
|
|
13896
|
+
* limit: 100
|
|
13897
|
+
* });
|
|
13898
|
+
*
|
|
13899
|
+
* console.log(`${recentRedeems.length} redeems in January 2024`);
|
|
13900
|
+
* ```
|
|
13901
|
+
*
|
|
13255
13902
|
* @example With Enriched Data
|
|
13256
13903
|
* ```typescript
|
|
13257
13904
|
* const { data: redeems } = await sdk.redemptions.getRedemptionRedeems(
|
|
@@ -13284,7 +13931,7 @@ class RedemptionManager {
|
|
|
13284
13931
|
* ```typescript
|
|
13285
13932
|
* // Admin operation - create discount redemption
|
|
13286
13933
|
* const newRedemption = await sdk.redemptions.createRedemption({
|
|
13287
|
-
*
|
|
13934
|
+
* name: '20% Off Weekend Stay',
|
|
13288
13935
|
* description: 'Get 20% off your next weekend booking at partner hotels',
|
|
13289
13936
|
* redemptionTypeId: 'discount-type-id',
|
|
13290
13937
|
* isActive: true,
|
|
@@ -13293,7 +13940,7 @@ class RedemptionManager {
|
|
|
13293
13940
|
* validUntil: '2024-08-31T23:59:59Z'
|
|
13294
13941
|
* });
|
|
13295
13942
|
*
|
|
13296
|
-
* console.log('New redemption created:', newRedemption.
|
|
13943
|
+
* console.log('New redemption created:', newRedemption.name);
|
|
13297
13944
|
* console.log('Redemption ID:', newRedemption.id);
|
|
13298
13945
|
*
|
|
13299
13946
|
* // Set redemption cost
|
|
@@ -13325,13 +13972,13 @@ class RedemptionManager {
|
|
|
13325
13972
|
* ```typescript
|
|
13326
13973
|
* // Admin operation - update redemption details
|
|
13327
13974
|
* const updated = await sdk.redemptions.updateRedemption('weekend-discount', {
|
|
13328
|
-
*
|
|
13975
|
+
* name: '25% Off Weekend Stay - Extended!',
|
|
13329
13976
|
* description: 'Enhanced discount - now 25% off weekend bookings',
|
|
13330
13977
|
* availableQuantity: 150, // Increased availability
|
|
13331
13978
|
* validUntil: '2024-09-30T23:59:59Z' // Extended validity
|
|
13332
13979
|
* });
|
|
13333
13980
|
*
|
|
13334
|
-
* console.log('Redemption updated:', updated.
|
|
13981
|
+
* console.log('Redemption updated:', updated.name);
|
|
13335
13982
|
* console.log('New quantity available:', updated.availableQuantity);
|
|
13336
13983
|
* ```
|
|
13337
13984
|
*/
|
|
@@ -16980,6 +17627,12 @@ class PersEventsClient {
|
|
|
16980
17627
|
}
|
|
16981
17628
|
}
|
|
16982
17629
|
}
|
|
17630
|
+
/**
|
|
17631
|
+
* Create a PERS Events client instance
|
|
17632
|
+
*/
|
|
17633
|
+
function createPersEventsClient(config) {
|
|
17634
|
+
return new PersEventsClient(config);
|
|
17635
|
+
}
|
|
16983
17636
|
|
|
16984
17637
|
/**
|
|
16985
17638
|
* Wallet Events Manager - Real-time blockchain events for user's wallets
|
|
@@ -17182,7 +17835,8 @@ class WalletEventsManager {
|
|
|
17182
17835
|
}
|
|
17183
17836
|
// Save subscriptions for auto-resubscribe on reconnect
|
|
17184
17837
|
this.lastSubscriptions.wallets = wallets;
|
|
17185
|
-
|
|
17838
|
+
const result = await this.client.subscribeWallets(wallets);
|
|
17839
|
+
return result;
|
|
17186
17840
|
}
|
|
17187
17841
|
/**
|
|
17188
17842
|
* Subscribe to chain events (Admin Dashboard)
|
|
@@ -17205,7 +17859,8 @@ class WalletEventsManager {
|
|
|
17205
17859
|
}
|
|
17206
17860
|
// Save subscriptions for auto-resubscribe on reconnect
|
|
17207
17861
|
this.lastSubscriptions.chains = chains;
|
|
17208
|
-
|
|
17862
|
+
const result = await this.client.subscribeChains(chains);
|
|
17863
|
+
return result;
|
|
17209
17864
|
}
|
|
17210
17865
|
/**
|
|
17211
17866
|
* Unsubscribe from wallet events
|
|
@@ -17515,6 +18170,24 @@ class PersSDK {
|
|
|
17515
18170
|
this.apiClient.setEvents(this._events);
|
|
17516
18171
|
// Auto-connect to wallet events on successful login (if enabled)
|
|
17517
18172
|
this.setupWalletEventsAutoConnect();
|
|
18173
|
+
// Auto-restore session from stored tokens (if enabled, default: true)
|
|
18174
|
+
this.setupAutoRestoreSession();
|
|
18175
|
+
}
|
|
18176
|
+
/**
|
|
18177
|
+
* Setup automatic session restoration from stored tokens
|
|
18178
|
+
* @internal
|
|
18179
|
+
*/
|
|
18180
|
+
setupAutoRestoreSession() {
|
|
18181
|
+
const config = this.apiClient.getConfig();
|
|
18182
|
+
// Check if autoRestoreSession is enabled (default: true)
|
|
18183
|
+
if (config.autoRestoreSession === false) {
|
|
18184
|
+
return;
|
|
18185
|
+
}
|
|
18186
|
+
// Fire-and-forget: restore session asynchronously
|
|
18187
|
+
// Events will be emitted on success/failure
|
|
18188
|
+
this.restoreSession().catch(() => {
|
|
18189
|
+
// Error already emitted via session_restoration_failed event
|
|
18190
|
+
});
|
|
17518
18191
|
}
|
|
17519
18192
|
/**
|
|
17520
18193
|
* Setup auto-connect for wallet events on authentication
|
|
@@ -17592,22 +18265,23 @@ class PersSDK {
|
|
|
17592
18265
|
/**
|
|
17593
18266
|
* Restore user session from stored tokens
|
|
17594
18267
|
*
|
|
17595
|
-
*
|
|
17596
|
-
*
|
|
17597
|
-
*
|
|
18268
|
+
* **Note:** This method is called automatically on SDK initialization when
|
|
18269
|
+
* `autoRestoreSession` is enabled (default: true). You only need to call this
|
|
18270
|
+
* manually if you disabled auto-restore or need to force a session refresh.
|
|
18271
|
+
*
|
|
18272
|
+
* Validates stored tokens and fetches user data to restore the session.
|
|
18273
|
+
* Emits `session_restored` event on success, `session_restoration_failed` on error.
|
|
17598
18274
|
*
|
|
17599
18275
|
* **Important:** Only works for USER and BUSINESS accounts. Admin/tenant
|
|
17600
18276
|
* accounts don't support user data fetching, so this will return null for them
|
|
17601
18277
|
* (though their tokens remain valid for API calls).
|
|
17602
18278
|
*
|
|
17603
|
-
* Emits auth domain success event when session is restored.
|
|
17604
|
-
*
|
|
17605
18279
|
* @returns Promise resolving to User data if session restored, null if no session or admin account
|
|
17606
18280
|
* @throws {PersError} If token validation or user fetch fails
|
|
17607
18281
|
*
|
|
17608
18282
|
* @example
|
|
17609
18283
|
* ```typescript
|
|
17610
|
-
* //
|
|
18284
|
+
* // Manual restore (only needed if autoRestoreSession: false)
|
|
17611
18285
|
* const user = await sdk.restoreSession();
|
|
17612
18286
|
* if (user) {
|
|
17613
18287
|
* console.log('Welcome back,', user.name);
|
|
@@ -17621,23 +18295,57 @@ class PersSDK {
|
|
|
17621
18295
|
}
|
|
17622
18296
|
// Check auth type - only restore session for user and business accounts
|
|
17623
18297
|
const authProvider = this.apiClient.getConfig().authProvider;
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
18298
|
+
const authType = authProvider?.getAuthType ? await authProvider.getAuthType() : exports.AccountOwnerType.USER;
|
|
18299
|
+
if (authType === exports.AccountOwnerType.TENANT) {
|
|
18300
|
+
// Ensure token is valid (refresh if expired) before fetching admin
|
|
18301
|
+
try {
|
|
18302
|
+
await this.auth.ensureValidToken();
|
|
18303
|
+
// Fetch current admin data
|
|
18304
|
+
const adminData = await this.auth.getCurrentAdmin();
|
|
18305
|
+
// Emit session restored for tenant admin
|
|
18306
|
+
this._events.emitSuccess({
|
|
18307
|
+
type: 'session_restored',
|
|
18308
|
+
domain: 'authentication',
|
|
18309
|
+
userMessage: 'Admin session restored successfully',
|
|
18310
|
+
details: { admin: adminData }
|
|
18311
|
+
});
|
|
17629
18312
|
return null;
|
|
17630
18313
|
}
|
|
18314
|
+
catch (error) {
|
|
18315
|
+
await this.auth.clearAuth();
|
|
18316
|
+
this._events.emitError({
|
|
18317
|
+
type: 'session_restoration_failed',
|
|
18318
|
+
domain: 'authentication',
|
|
18319
|
+
userMessage: 'Admin session restoration failed',
|
|
18320
|
+
code: 'SESSION_INVALID',
|
|
18321
|
+
details: { error }
|
|
18322
|
+
});
|
|
18323
|
+
throw error;
|
|
18324
|
+
}
|
|
17631
18325
|
}
|
|
17632
18326
|
try {
|
|
17633
|
-
//
|
|
18327
|
+
// Ensure token is valid (refresh if expired) before fetching user
|
|
18328
|
+
await this.auth.ensureValidToken();
|
|
18329
|
+
// For BUSINESS auth, only fetch business context (no access to /users/me)
|
|
18330
|
+
if (authType === exports.AccountOwnerType.BUSINESS) {
|
|
18331
|
+
const businessData = await this.auth.getCurrentBusiness();
|
|
18332
|
+
// Emit event with business data (no user data for business auth)
|
|
18333
|
+
this._events.emitSuccess({
|
|
18334
|
+
type: 'session_restored',
|
|
18335
|
+
domain: 'authentication',
|
|
18336
|
+
userMessage: 'Business session restored successfully',
|
|
18337
|
+
details: { business: businessData }
|
|
18338
|
+
});
|
|
18339
|
+
return null; // No user data for business auth
|
|
18340
|
+
}
|
|
18341
|
+
// For USER auth, fetch user data
|
|
17634
18342
|
const userData = await this.auth.getCurrentUser();
|
|
17635
|
-
// Emit event
|
|
18343
|
+
// Emit event with user data
|
|
17636
18344
|
this._events.emitSuccess({
|
|
17637
18345
|
type: 'session_restored',
|
|
17638
18346
|
domain: 'authentication',
|
|
17639
18347
|
userMessage: 'Session restored successfully',
|
|
17640
|
-
details: {
|
|
18348
|
+
details: { user: userData }
|
|
17641
18349
|
});
|
|
17642
18350
|
return userData;
|
|
17643
18351
|
}
|
|
@@ -18130,6 +18838,114 @@ class PersSDK {
|
|
|
18130
18838
|
return !!this.apiClient.getConfig().authProvider;
|
|
18131
18839
|
}
|
|
18132
18840
|
}
|
|
18841
|
+
/**
|
|
18842
|
+
* Factory function for creating PERS SDK
|
|
18843
|
+
*
|
|
18844
|
+
* @param httpClient Platform-specific HTTP client implementation
|
|
18845
|
+
* @param config SDK configuration options
|
|
18846
|
+
* @returns PERS SDK instance
|
|
18847
|
+
*/
|
|
18848
|
+
function createPersSDK(httpClient, config) {
|
|
18849
|
+
return new PersSDK(httpClient, config);
|
|
18850
|
+
}
|
|
18851
|
+
|
|
18852
|
+
class IndexedDBTokenStorage {
|
|
18853
|
+
constructor() {
|
|
18854
|
+
this.supportsObjects = true;
|
|
18855
|
+
this.dbPromise = null;
|
|
18856
|
+
if (typeof indexedDB === 'undefined') {
|
|
18857
|
+
console.warn('IndexedDB is not available in this environment');
|
|
18858
|
+
}
|
|
18859
|
+
}
|
|
18860
|
+
getDB() {
|
|
18861
|
+
if (this.dbPromise)
|
|
18862
|
+
return this.dbPromise;
|
|
18863
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
18864
|
+
if (typeof indexedDB === 'undefined') {
|
|
18865
|
+
return reject(new Error('IndexedDB not supported'));
|
|
18866
|
+
}
|
|
18867
|
+
const request = indexedDB.open(IndexedDBTokenStorage.DB_NAME, IndexedDBTokenStorage.DB_VERSION);
|
|
18868
|
+
request.onupgradeneeded = (event) => {
|
|
18869
|
+
const db = event.target.result;
|
|
18870
|
+
if (!db.objectStoreNames.contains(IndexedDBTokenStorage.STORE_NAME)) {
|
|
18871
|
+
db.createObjectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18872
|
+
}
|
|
18873
|
+
};
|
|
18874
|
+
request.onsuccess = (event) => {
|
|
18875
|
+
resolve(event.target.result);
|
|
18876
|
+
};
|
|
18877
|
+
request.onerror = (event) => {
|
|
18878
|
+
reject(event.target.error);
|
|
18879
|
+
};
|
|
18880
|
+
});
|
|
18881
|
+
return this.dbPromise;
|
|
18882
|
+
}
|
|
18883
|
+
async get(key) {
|
|
18884
|
+
try {
|
|
18885
|
+
const db = await this.getDB();
|
|
18886
|
+
return new Promise((resolve, reject) => {
|
|
18887
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readonly');
|
|
18888
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18889
|
+
const request = store.get(key);
|
|
18890
|
+
request.onsuccess = () => resolve(request.result || null);
|
|
18891
|
+
request.onerror = () => reject(request.error);
|
|
18892
|
+
});
|
|
18893
|
+
}
|
|
18894
|
+
catch (e) {
|
|
18895
|
+
console.warn('[IndexedDBTokenStorage] Failed to get key', key, e);
|
|
18896
|
+
return null;
|
|
18897
|
+
}
|
|
18898
|
+
}
|
|
18899
|
+
async set(key, value) {
|
|
18900
|
+
try {
|
|
18901
|
+
const db = await this.getDB();
|
|
18902
|
+
return new Promise((resolve, reject) => {
|
|
18903
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
|
|
18904
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18905
|
+
const request = store.put(value, key);
|
|
18906
|
+
request.onsuccess = () => resolve();
|
|
18907
|
+
request.onerror = () => reject(request.error);
|
|
18908
|
+
});
|
|
18909
|
+
}
|
|
18910
|
+
catch (e) {
|
|
18911
|
+
console.error('[IndexedDBTokenStorage] Failed to set key', key, e);
|
|
18912
|
+
throw e;
|
|
18913
|
+
}
|
|
18914
|
+
}
|
|
18915
|
+
async remove(key) {
|
|
18916
|
+
try {
|
|
18917
|
+
const db = await this.getDB();
|
|
18918
|
+
return new Promise((resolve, reject) => {
|
|
18919
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
|
|
18920
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18921
|
+
const request = store.delete(key);
|
|
18922
|
+
request.onsuccess = () => resolve();
|
|
18923
|
+
request.onerror = () => reject(request.error);
|
|
18924
|
+
});
|
|
18925
|
+
}
|
|
18926
|
+
catch (e) {
|
|
18927
|
+
console.warn('[IndexedDBTokenStorage] Failed to remove key', key, e);
|
|
18928
|
+
}
|
|
18929
|
+
}
|
|
18930
|
+
async clear() {
|
|
18931
|
+
try {
|
|
18932
|
+
const db = await this.getDB();
|
|
18933
|
+
return new Promise((resolve, reject) => {
|
|
18934
|
+
const transaction = db.transaction(IndexedDBTokenStorage.STORE_NAME, 'readwrite');
|
|
18935
|
+
const store = transaction.objectStore(IndexedDBTokenStorage.STORE_NAME);
|
|
18936
|
+
const request = store.clear();
|
|
18937
|
+
request.onsuccess = () => resolve();
|
|
18938
|
+
request.onerror = () => reject(request.error);
|
|
18939
|
+
});
|
|
18940
|
+
}
|
|
18941
|
+
catch (e) {
|
|
18942
|
+
console.warn('[IndexedDBTokenStorage] Failed to clear storage', e);
|
|
18943
|
+
}
|
|
18944
|
+
}
|
|
18945
|
+
}
|
|
18946
|
+
IndexedDBTokenStorage.DB_NAME = 'pers-sdk-storage';
|
|
18947
|
+
IndexedDBTokenStorage.STORE_NAME = 'auth-tokens';
|
|
18948
|
+
IndexedDBTokenStorage.DB_VERSION = 1;
|
|
18133
18949
|
|
|
18134
18950
|
/**
|
|
18135
18951
|
* Environment Detection Utility
|
|
@@ -18160,7 +18976,16 @@ function detectEnvironment() {
|
|
|
18160
18976
|
/**
|
|
18161
18977
|
* Global environment info (cached)
|
|
18162
18978
|
*/
|
|
18163
|
-
detectEnvironment();
|
|
18979
|
+
const environment = detectEnvironment();
|
|
18980
|
+
/**
|
|
18981
|
+
* Warns if environment might have bundling issues
|
|
18982
|
+
*/
|
|
18983
|
+
function warnIfProblematicEnvironment(feature) {
|
|
18984
|
+
if (environment.needsNodeStreamPolyfills) {
|
|
18985
|
+
console.warn(`[PERS SDK] ${feature} may require additional polyfills in web environments. ` +
|
|
18986
|
+
`If you encounter 'readable-stream' errors, ensure proper polyfills are configured.`);
|
|
18987
|
+
}
|
|
18988
|
+
}
|
|
18164
18989
|
|
|
18165
18990
|
/**
|
|
18166
18991
|
* AsyncStorage Token Storage for React Native Mobile Platforms
|
|
@@ -18490,21 +19315,45 @@ function createReactNativeAuthProvider(projectKey, config = {}) {
|
|
|
18490
19315
|
});
|
|
18491
19316
|
}
|
|
18492
19317
|
|
|
18493
|
-
//
|
|
18494
|
-
let
|
|
18495
|
-
|
|
19318
|
+
// Lazy-loaded crypto module - initialized on first use to avoid native module timing issues
|
|
19319
|
+
let _crypto = null;
|
|
19320
|
+
let _cryptoInitialized = false;
|
|
19321
|
+
/**
|
|
19322
|
+
* Lazily load the crypto module.
|
|
19323
|
+
*
|
|
19324
|
+
* Supports customer pre-initialization via global.__QuickCrypto for environments
|
|
19325
|
+
* where native module timing is unpredictable (e.g., certain React Native setups).
|
|
19326
|
+
*
|
|
19327
|
+
* @returns The crypto module or null if unavailable
|
|
19328
|
+
*/
|
|
19329
|
+
function getCrypto() {
|
|
19330
|
+
if (_cryptoInitialized) {
|
|
19331
|
+
return _crypto;
|
|
19332
|
+
}
|
|
19333
|
+
_cryptoInitialized = true;
|
|
19334
|
+
if (reactNative.Platform.OS === 'web') {
|
|
19335
|
+
// On Web, we shouldn't be using this provider (Core SDK has WebDPoPProvider)
|
|
19336
|
+
_crypto = {
|
|
19337
|
+
generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); },
|
|
19338
|
+
randomUUID: () => globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`
|
|
19339
|
+
};
|
|
19340
|
+
return _crypto;
|
|
19341
|
+
}
|
|
19342
|
+
// Check for customer pre-initialized global (for native module timing issues)
|
|
19343
|
+
if (typeof global !== 'undefined' && global.__QuickCrypto) {
|
|
19344
|
+
_crypto = global.__QuickCrypto;
|
|
19345
|
+
return _crypto;
|
|
19346
|
+
}
|
|
19347
|
+
// Standard lazy load
|
|
18496
19348
|
try {
|
|
18497
|
-
|
|
19349
|
+
_crypto = require('react-native-quick-crypto').default || require('react-native-quick-crypto');
|
|
18498
19350
|
}
|
|
18499
19351
|
catch (e) {
|
|
18500
|
-
console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto', e);
|
|
19352
|
+
console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto.', e);
|
|
19353
|
+
console.warn('Tip: If you encounter timing issues, pre-initialize in index.js: global.__QuickCrypto = require("react-native-quick-crypto")');
|
|
19354
|
+
_crypto = null;
|
|
18501
19355
|
}
|
|
18502
|
-
|
|
18503
|
-
else {
|
|
18504
|
-
// on Web, we shouldn't be using this provider anyway (Core SDK has WebDPoPProvider)
|
|
18505
|
-
crypto$1 = {
|
|
18506
|
-
generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); }
|
|
18507
|
-
};
|
|
19356
|
+
return _crypto;
|
|
18508
19357
|
}
|
|
18509
19358
|
class ReactNativeDPoPProvider {
|
|
18510
19359
|
/**
|
|
@@ -18514,13 +19363,17 @@ class ReactNativeDPoPProvider {
|
|
|
18514
19363
|
* Falls back to Node.js crypto API on iOS if needed.
|
|
18515
19364
|
*/
|
|
18516
19365
|
async generateKeyPair(options) {
|
|
19366
|
+
const crypto = getCrypto();
|
|
19367
|
+
if (!crypto) {
|
|
19368
|
+
throw new Error('Crypto module not available. Install react-native-quick-crypto or pre-initialize global.__QuickCrypto');
|
|
19369
|
+
}
|
|
18517
19370
|
// Try WebCrypto API first (works on both iOS and Android)
|
|
18518
|
-
if (crypto
|
|
19371
|
+
if (crypto.subtle) {
|
|
18519
19372
|
try {
|
|
18520
|
-
const keyPair = await crypto
|
|
19373
|
+
const keyPair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, // extractable - required for JWK export
|
|
18521
19374
|
['sign', 'verify']);
|
|
18522
|
-
const publicKeyJwk = await crypto
|
|
18523
|
-
const privateKeyJwk = await crypto
|
|
19375
|
+
const publicKeyJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
|
|
19376
|
+
const privateKeyJwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey);
|
|
18524
19377
|
return {
|
|
18525
19378
|
publicKey: publicKeyJwk,
|
|
18526
19379
|
privateKey: privateKeyJwk
|
|
@@ -18532,7 +19385,7 @@ class ReactNativeDPoPProvider {
|
|
|
18532
19385
|
}
|
|
18533
19386
|
// Fallback: Node.js crypto API (works on iOS)
|
|
18534
19387
|
return new Promise((resolve, reject) => {
|
|
18535
|
-
crypto
|
|
19388
|
+
crypto.generateKeyPair('ec', { namedCurve: 'P-256' }, (err, publicKey, privateKey) => {
|
|
18536
19389
|
if (err)
|
|
18537
19390
|
return reject(err);
|
|
18538
19391
|
try {
|
|
@@ -18547,6 +19400,10 @@ class ReactNativeDPoPProvider {
|
|
|
18547
19400
|
});
|
|
18548
19401
|
}
|
|
18549
19402
|
async signProof(payload, keyPair) {
|
|
19403
|
+
const crypto = getCrypto();
|
|
19404
|
+
if (!crypto) {
|
|
19405
|
+
throw new Error('Crypto module not available for signing');
|
|
19406
|
+
}
|
|
18550
19407
|
// 1. Construct Header
|
|
18551
19408
|
const header = {
|
|
18552
19409
|
typ: 'dpop+jwt',
|
|
@@ -18557,17 +19414,17 @@ class ReactNativeDPoPProvider {
|
|
|
18557
19414
|
const finalPayload = {
|
|
18558
19415
|
...payload,
|
|
18559
19416
|
iat: payload.iat || Math.floor(Date.now() / 1000),
|
|
18560
|
-
jti: payload.jti || crypto
|
|
19417
|
+
jti: payload.jti || (crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`)
|
|
18561
19418
|
};
|
|
18562
19419
|
// 3. Encode
|
|
18563
19420
|
const b64Header = this.base64Url(JSON.stringify(header));
|
|
18564
19421
|
const b64Payload = this.base64Url(JSON.stringify(finalPayload));
|
|
18565
19422
|
const signingInput = `${b64Header}.${b64Payload}`;
|
|
18566
19423
|
// 4. Sign - try WebCrypto first, fallback to Node.js crypto
|
|
18567
|
-
if (crypto
|
|
19424
|
+
if (crypto.subtle) {
|
|
18568
19425
|
try {
|
|
18569
|
-
const privateKey = await crypto
|
|
18570
|
-
const signatureBuffer = await crypto
|
|
19426
|
+
const privateKey = await crypto.subtle.importKey('jwk', keyPair.privateKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
|
|
19427
|
+
const signatureBuffer = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, privateKey, buffer.Buffer.from(signingInput));
|
|
18571
19428
|
// WebCrypto returns signature in IEEE P1363 format (r || s), which is what we need for JWT
|
|
18572
19429
|
return `${signingInput}.${this.base64UrlBuffer(buffer.Buffer.from(signatureBuffer))}`;
|
|
18573
19430
|
}
|
|
@@ -18576,24 +19433,28 @@ class ReactNativeDPoPProvider {
|
|
|
18576
19433
|
}
|
|
18577
19434
|
}
|
|
18578
19435
|
// Fallback: Node.js crypto API
|
|
18579
|
-
const sign = crypto
|
|
19436
|
+
const sign = crypto.createSign('SHA256');
|
|
18580
19437
|
sign.update(signingInput);
|
|
18581
|
-
const privateKeyObj = crypto
|
|
19438
|
+
const privateKeyObj = crypto.createPrivateKey({ key: keyPair.privateKey, format: 'jwk' });
|
|
18582
19439
|
const signature = sign.sign(privateKeyObj);
|
|
18583
19440
|
return `${signingInput}.${this.base64UrlBuffer(signature)}`;
|
|
18584
19441
|
}
|
|
18585
19442
|
async hashAccessToken(accessToken) {
|
|
19443
|
+
const crypto = getCrypto();
|
|
19444
|
+
if (!crypto) {
|
|
19445
|
+
throw new Error('Crypto module not available for hashing');
|
|
19446
|
+
}
|
|
18586
19447
|
// Try WebCrypto first
|
|
18587
|
-
if (crypto
|
|
19448
|
+
if (crypto.subtle) {
|
|
18588
19449
|
try {
|
|
18589
|
-
const hashBuffer = await crypto
|
|
19450
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer.Buffer.from(accessToken));
|
|
18590
19451
|
return this.base64UrlBuffer(buffer.Buffer.from(hashBuffer));
|
|
18591
19452
|
}
|
|
18592
19453
|
catch (err) {
|
|
18593
19454
|
// Fallback to Node.js crypto
|
|
18594
19455
|
}
|
|
18595
19456
|
}
|
|
18596
|
-
const hash = crypto
|
|
19457
|
+
const hash = crypto.createHash('sha256').update(accessToken).digest();
|
|
18597
19458
|
return this.base64UrlBuffer(hash);
|
|
18598
19459
|
}
|
|
18599
19460
|
// --- Helpers ---
|
|
@@ -18857,22 +19718,12 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
18857
19718
|
if (!sdk)
|
|
18858
19719
|
return;
|
|
18859
19720
|
const unsubscribe = sdk.events.subscribe((event) => {
|
|
18860
|
-
|
|
18861
|
-
|
|
18862
|
-
|
|
18863
|
-
|
|
18864
|
-
|
|
18865
|
-
|
|
18866
|
-
const userId = event.details?.userId;
|
|
18867
|
-
if (userId) {
|
|
18868
|
-
// User ID available, fetch user data
|
|
18869
|
-
sdk.users.getCurrentUser()
|
|
18870
|
-
.then(userData => {
|
|
18871
|
-
setAuthenticationState(userData, true);
|
|
18872
|
-
})
|
|
18873
|
-
.catch(error => {
|
|
18874
|
-
console.error('[PersSDK] Failed to sync restored session:', error);
|
|
18875
|
-
});
|
|
19721
|
+
// Handle both session restoration and fresh login
|
|
19722
|
+
if (event.type === 'session_restored' || event.type === 'login_success') {
|
|
19723
|
+
const details = event.details;
|
|
19724
|
+
if (details?.user) {
|
|
19725
|
+
console.log(`[PersSDK] ${event.type}: syncing user state`);
|
|
19726
|
+
setAuthenticationState(details.user, true);
|
|
18876
19727
|
}
|
|
18877
19728
|
}
|
|
18878
19729
|
// Session restoration failed or auth error - clear React state
|
|
@@ -18880,7 +19731,7 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
18880
19731
|
console.log('[PersSDK] Authentication failed - clearing session');
|
|
18881
19732
|
setAuthenticationState(null, false);
|
|
18882
19733
|
}
|
|
18883
|
-
}, { domains: ['authentication'] });
|
|
19734
|
+
}, { domains: ['authentication'], replay: true });
|
|
18884
19735
|
return () => {
|
|
18885
19736
|
unsubscribe();
|
|
18886
19737
|
};
|
|
@@ -19006,21 +19857,15 @@ const useAuth = () => {
|
|
|
19006
19857
|
// Set token in auth provider
|
|
19007
19858
|
await authProvider.setAccessToken(jwtToken);
|
|
19008
19859
|
// Perform login using the manager
|
|
19860
|
+
// SDK emits login_success event with user data - PersSDKProvider handles state sync
|
|
19009
19861
|
const result = await sdk.auth.loginWithToken(jwtToken, userType);
|
|
19010
|
-
|
|
19011
|
-
if (userData) {
|
|
19012
|
-
setAuthenticationState(userData, true);
|
|
19013
|
-
return result;
|
|
19014
|
-
}
|
|
19015
|
-
else {
|
|
19016
|
-
throw new Error('No user data returned from login');
|
|
19017
|
-
}
|
|
19862
|
+
return result;
|
|
19018
19863
|
}
|
|
19019
19864
|
catch (error) {
|
|
19020
19865
|
console.error('Login failed:', error);
|
|
19021
19866
|
throw error;
|
|
19022
19867
|
}
|
|
19023
|
-
}, [sdk, authProvider
|
|
19868
|
+
}, [sdk, authProvider]);
|
|
19024
19869
|
/**
|
|
19025
19870
|
* Logs in a user using raw user data (direct authentication bypass)
|
|
19026
19871
|
*
|
|
@@ -19039,24 +19884,18 @@ const useAuth = () => {
|
|
|
19039
19884
|
}
|
|
19040
19885
|
try {
|
|
19041
19886
|
// Use the raw data login from the auth manager
|
|
19887
|
+
// SDK emits login_success event with user data - PersSDKProvider handles state sync
|
|
19042
19888
|
const result = await sdk.auth.loginWithRawData(rawUserData);
|
|
19043
19889
|
// Set token from result
|
|
19044
19890
|
if (result.accessToken) {
|
|
19045
19891
|
await authProvider.setAccessToken(result.accessToken);
|
|
19046
19892
|
}
|
|
19047
|
-
const userData = result.user;
|
|
19048
|
-
if (userData) {
|
|
19049
|
-
setAuthenticationState(userData, true);
|
|
19050
|
-
}
|
|
19051
|
-
else {
|
|
19052
|
-
throw new Error('No user data returned from raw data login');
|
|
19053
|
-
}
|
|
19054
19893
|
}
|
|
19055
19894
|
catch (error) {
|
|
19056
19895
|
console.error('Raw data login failed:', error);
|
|
19057
19896
|
throw error;
|
|
19058
19897
|
}
|
|
19059
|
-
}, [sdk, authProvider
|
|
19898
|
+
}, [sdk, authProvider]);
|
|
19060
19899
|
/**
|
|
19061
19900
|
* Logs out the current user and clears authentication state
|
|
19062
19901
|
*
|
|
@@ -40191,6 +41030,581 @@ const useWeb3 = () => {
|
|
|
40191
41030
|
};
|
|
40192
41031
|
};
|
|
40193
41032
|
|
|
41033
|
+
/**
|
|
41034
|
+
* Token SDK - Class-based Promise SDK for Token Operations
|
|
41035
|
+
*
|
|
41036
|
+
* Modern, performant SDK with direct method access and excellent TypeScript support.
|
|
41037
|
+
* Optimized for bundle size, performance, and developer experience.
|
|
41038
|
+
*
|
|
41039
|
+
* Usage:
|
|
41040
|
+
* const tokenSDK = new TokenSDK(apiClient);
|
|
41041
|
+
* const tokens = await tokenSDK.getTokens();
|
|
41042
|
+
* const creditToken = await tokenSDK.getActiveCreditToken();
|
|
41043
|
+
*/
|
|
41044
|
+
class TokenSDK {
|
|
41045
|
+
constructor(apiClient) {
|
|
41046
|
+
this.tokenApi = new TokenApi(apiClient);
|
|
41047
|
+
this.tokenService = new TokenService(this.tokenApi);
|
|
41048
|
+
}
|
|
41049
|
+
// ==========================================
|
|
41050
|
+
// CONVENIENCE METHODS - Direct Promise API
|
|
41051
|
+
// ==========================================
|
|
41052
|
+
// Simplified method names for better developer experience
|
|
41053
|
+
/**
|
|
41054
|
+
* Get all tokens
|
|
41055
|
+
* @returns Promise with all available tokens
|
|
41056
|
+
*/
|
|
41057
|
+
async getTokens() {
|
|
41058
|
+
return this.tokenService.getRemoteTokens();
|
|
41059
|
+
}
|
|
41060
|
+
/**
|
|
41061
|
+
* Get all token types
|
|
41062
|
+
* @returns Promise with all available token types
|
|
41063
|
+
*/
|
|
41064
|
+
async getTokenTypes() {
|
|
41065
|
+
return this.tokenService.getRemoteTokenTypes();
|
|
41066
|
+
}
|
|
41067
|
+
/**
|
|
41068
|
+
* Get the active credit token
|
|
41069
|
+
* @returns Promise with the current active credit token
|
|
41070
|
+
*/
|
|
41071
|
+
async getActiveCreditToken() {
|
|
41072
|
+
return this.tokenService.getRemoteActiveCreditToken();
|
|
41073
|
+
}
|
|
41074
|
+
/**
|
|
41075
|
+
* Get all reward tokens
|
|
41076
|
+
* @returns Promise with all reward tokens
|
|
41077
|
+
*/
|
|
41078
|
+
async getRewardTokens() {
|
|
41079
|
+
return this.tokenService.getRemoteRewardTokens();
|
|
41080
|
+
}
|
|
41081
|
+
/**
|
|
41082
|
+
* Get all status tokens
|
|
41083
|
+
* @returns Promise with all status tokens
|
|
41084
|
+
*/
|
|
41085
|
+
async getStatusTokens() {
|
|
41086
|
+
return this.tokenService.getRemoteStatusTokens();
|
|
41087
|
+
}
|
|
41088
|
+
/**
|
|
41089
|
+
* Get token by contract address
|
|
41090
|
+
* @param contractAddress - The contract address to search for
|
|
41091
|
+
* @param contractTokenId - Optional contract token ID
|
|
41092
|
+
* @returns Promise with the matching token
|
|
41093
|
+
*/
|
|
41094
|
+
async getTokenByContract(contractAddress, contractTokenId = null) {
|
|
41095
|
+
return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
41096
|
+
}
|
|
41097
|
+
// ==========================================
|
|
41098
|
+
// ADMIN METHODS - Token Management
|
|
41099
|
+
// ==========================================
|
|
41100
|
+
/**
|
|
41101
|
+
* Create token metadata
|
|
41102
|
+
* @param tokenId - The token ID
|
|
41103
|
+
* @param tokenData - The token metadata request data
|
|
41104
|
+
* @returns Promise with the updated token
|
|
41105
|
+
*/
|
|
41106
|
+
async createTokenMetadata(tokenId, tokenData) {
|
|
41107
|
+
return this.tokenService.createTokenMetadata(tokenId, tokenData);
|
|
41108
|
+
}
|
|
41109
|
+
/**
|
|
41110
|
+
* Update token metadata (ERC721 only)
|
|
41111
|
+
* Note: Existing minted NFTs retain their original metadata - this only affects future mints
|
|
41112
|
+
* @param metadataId - The metadata ID to update
|
|
41113
|
+
* @param tokenData - The token metadata request data
|
|
41114
|
+
* @returns Promise with the updated metadata
|
|
41115
|
+
*/
|
|
41116
|
+
async updateTokenMetadata(metadataId, tokenData) {
|
|
41117
|
+
return this.tokenService.updateTokenMetadata(metadataId, tokenData);
|
|
41118
|
+
}
|
|
41119
|
+
/**
|
|
41120
|
+
* Toggle token active status
|
|
41121
|
+
* @param tokenId - The token ID to toggle
|
|
41122
|
+
* @returns Promise with the updated token
|
|
41123
|
+
*/
|
|
41124
|
+
async toggleTokenActive(tokenId) {
|
|
41125
|
+
return this.tokenService.toggleTokenActive(tokenId);
|
|
41126
|
+
}
|
|
41127
|
+
/**
|
|
41128
|
+
* Create a new token
|
|
41129
|
+
* @param tokenData - The token creation data
|
|
41130
|
+
* @returns Promise with the created token
|
|
41131
|
+
*/
|
|
41132
|
+
async createToken(tokenData) {
|
|
41133
|
+
return this.tokenService.createToken(tokenData);
|
|
41134
|
+
}
|
|
41135
|
+
/**
|
|
41136
|
+
* Update an existing token
|
|
41137
|
+
* @param tokenId - The token ID to update
|
|
41138
|
+
* @param tokenData - The token update data
|
|
41139
|
+
* @returns Promise with the updated token
|
|
41140
|
+
*/
|
|
41141
|
+
async updateToken(tokenId, tokenData) {
|
|
41142
|
+
return this.tokenService.updateToken(tokenId, tokenData);
|
|
41143
|
+
}
|
|
41144
|
+
/**
|
|
41145
|
+
* Set mainnet contract address for a token
|
|
41146
|
+
* @param tokenId - The token ID
|
|
41147
|
+
* @param contractAddress - The contract address
|
|
41148
|
+
* @param chainId - The blockchain chain ID
|
|
41149
|
+
* @returns Promise with the updated token
|
|
41150
|
+
*/
|
|
41151
|
+
async setMainnetContract(tokenId, contractAddress, chainId) {
|
|
41152
|
+
return this.tokenService.setMainnetContract(tokenId, contractAddress, chainId);
|
|
41153
|
+
}
|
|
41154
|
+
/**
|
|
41155
|
+
* Toggle token metadata status
|
|
41156
|
+
* @param metadataId - The metadata ID to toggle
|
|
41157
|
+
* @returns Promise with the updated metadata
|
|
41158
|
+
*/
|
|
41159
|
+
async toggleTokenMetadataStatus(metadataId) {
|
|
41160
|
+
return this.tokenService.toggleTokenMetadataStatus(metadataId);
|
|
41161
|
+
}
|
|
41162
|
+
/**
|
|
41163
|
+
* Create a new token type
|
|
41164
|
+
* @param tokenType - The token type data
|
|
41165
|
+
* @returns Promise with the created token type
|
|
41166
|
+
*/
|
|
41167
|
+
async createTokenType(tokenType) {
|
|
41168
|
+
return this.tokenService.createTokenType(tokenType);
|
|
41169
|
+
}
|
|
41170
|
+
// ==========================================
|
|
41171
|
+
// ADVANCED ACCESS - For Complex Operations
|
|
41172
|
+
// ==========================================
|
|
41173
|
+
/**
|
|
41174
|
+
* Get direct access to the token service for advanced operations
|
|
41175
|
+
* @returns The underlying TokenService instance
|
|
41176
|
+
*/
|
|
41177
|
+
getTokenService() {
|
|
41178
|
+
return this.tokenService;
|
|
41179
|
+
}
|
|
41180
|
+
/**
|
|
41181
|
+
* Get direct access to the token API for low-level operations
|
|
41182
|
+
* @returns The underlying TokenApi instance
|
|
41183
|
+
*/
|
|
41184
|
+
getTokenApi() {
|
|
41185
|
+
return this.tokenApi;
|
|
41186
|
+
}
|
|
41187
|
+
}
|
|
41188
|
+
|
|
41189
|
+
// ==========================================
|
|
41190
|
+
// TOKEN UTILITY FUNCTIONS
|
|
41191
|
+
// ==========================================
|
|
41192
|
+
/**
|
|
41193
|
+
* Get metadata from a token unit response
|
|
41194
|
+
* Handles metadata-based tokens (ERC-1155 with metadata array)
|
|
41195
|
+
*
|
|
41196
|
+
* For metadata-based tokens:
|
|
41197
|
+
* - Returns the metadata object from token.metadata array
|
|
41198
|
+
* - Uses provided incrementalId or tokenMetadataIncrementalId from tokenUnit
|
|
41199
|
+
* - Defaults to index 0 if no incrementalId specified
|
|
41200
|
+
*
|
|
41201
|
+
* For standard tokens (no metadata):
|
|
41202
|
+
* - Returns null
|
|
41203
|
+
*
|
|
41204
|
+
* @param tokenUnit - The token unit from campaign or balance
|
|
41205
|
+
* @param incrementalId - Optional metadata index to use (overrides tokenUnit.tokenMetadataIncrementalId)
|
|
41206
|
+
* @returns Token metadata object or null
|
|
41207
|
+
*
|
|
41208
|
+
* @example
|
|
41209
|
+
* ```typescript
|
|
41210
|
+
* // Get metadata using tokenUnit's incrementalId
|
|
41211
|
+
* const tokenUnit = {
|
|
41212
|
+
* tokenMetadataIncrementalId: 3,
|
|
41213
|
+
* token: {
|
|
41214
|
+
* metadata: [
|
|
41215
|
+
* { name: 'Bronze Pass', imageUrl: '...' },
|
|
41216
|
+
* { name: 'Silver Pass', imageUrl: '...' },
|
|
41217
|
+
* { name: 'Gold Pass', imageUrl: '...' }
|
|
41218
|
+
* ]
|
|
41219
|
+
* }
|
|
41220
|
+
* };
|
|
41221
|
+
* const metadata = getMetadataFromTokenUnitResponse(tokenUnit);
|
|
41222
|
+
* // Returns: { name: 'Bronze Pass', imageUrl: '...' } (index 0 - default)
|
|
41223
|
+
*
|
|
41224
|
+
* // Override incrementalId
|
|
41225
|
+
* const metadata2 = getMetadataFromTokenUnitResponse(tokenUnit, 2);
|
|
41226
|
+
* // Returns: { name: 'Gold Pass', imageUrl: '...' } (index 2)
|
|
41227
|
+
*
|
|
41228
|
+
* // Standard token (no metadata)
|
|
41229
|
+
* const tokenUnit2 = {
|
|
41230
|
+
* token: { symbol: 'VQP', name: 'Visit Qatar Points' }
|
|
41231
|
+
* };
|
|
41232
|
+
* const metadata3 = getMetadataFromTokenUnitResponse(tokenUnit2);
|
|
41233
|
+
* // Returns: null
|
|
41234
|
+
*
|
|
41235
|
+
* // Display name with fallback
|
|
41236
|
+
* const displayName = metadata?.name || tokenUnit.token?.symbol || 'Reward';
|
|
41237
|
+
*
|
|
41238
|
+
* // Display image
|
|
41239
|
+
* const imageUrl = metadata?.imageUrl;
|
|
41240
|
+
* ```
|
|
41241
|
+
*/
|
|
41242
|
+
function getMetadataFromTokenUnitResponse(tokenUnit, incrementalId) {
|
|
41243
|
+
// If no metadata array exists, return null
|
|
41244
|
+
if (!tokenUnit.token?.metadata || tokenUnit.token.metadata.length === 0) {
|
|
41245
|
+
return null;
|
|
41246
|
+
}
|
|
41247
|
+
const metadata = tokenUnit.token.metadata;
|
|
41248
|
+
// Use provided incrementalId or tokenUnit's incrementalId to find matching metadata
|
|
41249
|
+
const targetId = incrementalId ?? tokenUnit.tokenMetadataIncrementalId;
|
|
41250
|
+
if (targetId !== undefined) {
|
|
41251
|
+
const found = metadata.find((m) => m.incrementalId === targetId);
|
|
41252
|
+
if (found)
|
|
41253
|
+
return found;
|
|
41254
|
+
}
|
|
41255
|
+
// Fallback to first metadata item
|
|
41256
|
+
return metadata[0];
|
|
41257
|
+
}
|
|
41258
|
+
|
|
41259
|
+
/**
|
|
41260
|
+
* Abstract Base Token Service - Explicit Initialization Pattern
|
|
41261
|
+
*
|
|
41262
|
+
* Platform-agnostic token operations with Promise-based API.
|
|
41263
|
+
* Framework services extend this and control initialization lifecycle.
|
|
41264
|
+
*
|
|
41265
|
+
* Benefits:
|
|
41266
|
+
* - Explicit initialization control
|
|
41267
|
+
* - Better error boundaries
|
|
41268
|
+
* - Clear lifecycle management
|
|
41269
|
+
* - Testable initialization state
|
|
41270
|
+
* - Zero framework dependencies
|
|
41271
|
+
*/
|
|
41272
|
+
class BaseTokenService {
|
|
41273
|
+
constructor() {
|
|
41274
|
+
this._isInitialized = false;
|
|
41275
|
+
}
|
|
41276
|
+
// ==========================================
|
|
41277
|
+
// INITIALIZATION LIFECYCLE
|
|
41278
|
+
// ==========================================
|
|
41279
|
+
/**
|
|
41280
|
+
* LIFECYCLE: Initialize token service with API client
|
|
41281
|
+
* Must be called before using any token operations
|
|
41282
|
+
*/
|
|
41283
|
+
initializeTokenService(apiClient) {
|
|
41284
|
+
if (!apiClient) {
|
|
41285
|
+
throw new Error('Cannot initialize TokenService: apiClient is null or undefined');
|
|
41286
|
+
}
|
|
41287
|
+
if (!this._isInitialized) {
|
|
41288
|
+
this._tokenApi = new TokenApi(apiClient);
|
|
41289
|
+
this._tokenBusinessService = new TokenService(this._tokenApi);
|
|
41290
|
+
this._isInitialized = true;
|
|
41291
|
+
}
|
|
41292
|
+
}
|
|
41293
|
+
/**
|
|
41294
|
+
* LIFECYCLE: Check if token service is initialized
|
|
41295
|
+
*/
|
|
41296
|
+
get isTokenServiceInitialized() {
|
|
41297
|
+
return this._isInitialized;
|
|
41298
|
+
}
|
|
41299
|
+
/**
|
|
41300
|
+
* INTERNAL: Get token business service (throws if not initialized)
|
|
41301
|
+
*/
|
|
41302
|
+
get tokenBusinessService() {
|
|
41303
|
+
if (!this._tokenBusinessService) {
|
|
41304
|
+
throw new Error('TokenService not initialized. Call initializeTokenService(apiClient) first.');
|
|
41305
|
+
}
|
|
41306
|
+
return this._tokenBusinessService;
|
|
41307
|
+
}
|
|
41308
|
+
// ==========================================
|
|
41309
|
+
// PUBLIC OPERATIONS
|
|
41310
|
+
// ==========================================
|
|
41311
|
+
/**
|
|
41312
|
+
* PUBLIC: Get all remote tokens
|
|
41313
|
+
*/
|
|
41314
|
+
getRemoteTokens() {
|
|
41315
|
+
return this.tokenBusinessService.getRemoteTokens();
|
|
41316
|
+
}
|
|
41317
|
+
/**
|
|
41318
|
+
* PUBLIC: Get all remote token types
|
|
41319
|
+
*/
|
|
41320
|
+
getRemoteTokenTypes() {
|
|
41321
|
+
return this.tokenBusinessService.getRemoteTokenTypes();
|
|
41322
|
+
}
|
|
41323
|
+
/**
|
|
41324
|
+
* PUBLIC: Get active credit token
|
|
41325
|
+
*/
|
|
41326
|
+
getRemoteActiveCreditToken() {
|
|
41327
|
+
return this.tokenBusinessService.getRemoteActiveCreditToken();
|
|
41328
|
+
}
|
|
41329
|
+
/**
|
|
41330
|
+
* PUBLIC: Get reward tokens
|
|
41331
|
+
*/
|
|
41332
|
+
getRemoteRewardTokens() {
|
|
41333
|
+
return this.tokenBusinessService.getRemoteRewardTokens();
|
|
41334
|
+
}
|
|
41335
|
+
/**
|
|
41336
|
+
* PUBLIC: Get status tokens
|
|
41337
|
+
*/
|
|
41338
|
+
getRemoteStatusTokens() {
|
|
41339
|
+
return this.tokenBusinessService.getRemoteStatusTokens();
|
|
41340
|
+
}
|
|
41341
|
+
/**
|
|
41342
|
+
* PUBLIC: Get token by contract address
|
|
41343
|
+
*/
|
|
41344
|
+
getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
41345
|
+
return this.tokenBusinessService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
41346
|
+
}
|
|
41347
|
+
// ==========================================
|
|
41348
|
+
// ADMIN OPERATIONS
|
|
41349
|
+
// ==========================================
|
|
41350
|
+
/**
|
|
41351
|
+
* ADMIN: Create token metadata
|
|
41352
|
+
*/
|
|
41353
|
+
createTokenMetadata(tokenId, tokenData) {
|
|
41354
|
+
return this.tokenBusinessService.createTokenMetadata(tokenId, tokenData);
|
|
41355
|
+
}
|
|
41356
|
+
/**
|
|
41357
|
+
* ADMIN: Toggle token active status
|
|
41358
|
+
*/
|
|
41359
|
+
toggleTokenActive(tokenId) {
|
|
41360
|
+
return this.tokenBusinessService.toggleTokenActive(tokenId);
|
|
41361
|
+
}
|
|
41362
|
+
/**
|
|
41363
|
+
* ADMIN: Create new token
|
|
41364
|
+
*/
|
|
41365
|
+
createToken(tokenData) {
|
|
41366
|
+
return this.tokenBusinessService.createToken(tokenData);
|
|
41367
|
+
}
|
|
41368
|
+
/**
|
|
41369
|
+
* ADMIN: Update token
|
|
41370
|
+
*/
|
|
41371
|
+
updateToken(tokenId, tokenData) {
|
|
41372
|
+
return this.tokenBusinessService.updateToken(tokenId, tokenData);
|
|
41373
|
+
}
|
|
41374
|
+
/**
|
|
41375
|
+
* ADMIN: Set mainnet contract address
|
|
41376
|
+
*/
|
|
41377
|
+
setMainnetContract(tokenId, contractAddress, chainId) {
|
|
41378
|
+
return this.tokenBusinessService.setMainnetContract(tokenId, contractAddress, chainId);
|
|
41379
|
+
}
|
|
41380
|
+
/**
|
|
41381
|
+
* ADMIN: Toggle token metadata status
|
|
41382
|
+
*/
|
|
41383
|
+
toggleTokenMetadataStatus(metadataId) {
|
|
41384
|
+
return this.tokenBusinessService.toggleTokenMetadataStatus(metadataId);
|
|
41385
|
+
}
|
|
41386
|
+
/**
|
|
41387
|
+
* ADMIN: Create token type
|
|
41388
|
+
*/
|
|
41389
|
+
createTokenType(tokenType) {
|
|
41390
|
+
return this.tokenBusinessService.createTokenType(tokenType);
|
|
41391
|
+
}
|
|
41392
|
+
}
|
|
41393
|
+
|
|
41394
|
+
/**
|
|
41395
|
+
* Node.js HTTP Client adapter for PERS SDK
|
|
41396
|
+
*
|
|
41397
|
+
* Simple HTTP client for Node.js environments using native fetch (Node 18+) or a polyfill.
|
|
41398
|
+
* Works with Express, Next.js API routes, or any Node.js-based backend.
|
|
41399
|
+
*
|
|
41400
|
+
* @example
|
|
41401
|
+
* ```typescript
|
|
41402
|
+
* import { createPersSDK } from '@explorins/pers-sdk';
|
|
41403
|
+
* import { NodeHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
41404
|
+
*
|
|
41405
|
+
* const sdk = createPersSDK(
|
|
41406
|
+
* new NodeHttpClientAdapter(),
|
|
41407
|
+
* { environment: 'production', apiProjectKey: 'your-key' }
|
|
41408
|
+
* );
|
|
41409
|
+
*
|
|
41410
|
+
* // Use managers directly
|
|
41411
|
+
* await sdk.user.getCurrentUser();
|
|
41412
|
+
* ```
|
|
41413
|
+
*/
|
|
41414
|
+
class NodeHttpClientAdapter {
|
|
41415
|
+
async get(url, options = {}) {
|
|
41416
|
+
return this.request(url, 'GET', undefined, options);
|
|
41417
|
+
}
|
|
41418
|
+
async post(url, data = null, options = {}) {
|
|
41419
|
+
return this.request(url, 'POST', data, options);
|
|
41420
|
+
}
|
|
41421
|
+
async put(url, data = null, options = {}) {
|
|
41422
|
+
return this.request(url, 'PUT', data, options);
|
|
41423
|
+
}
|
|
41424
|
+
async delete(url, options = {}) {
|
|
41425
|
+
return this.request(url, 'DELETE', undefined, options);
|
|
41426
|
+
}
|
|
41427
|
+
async patch(url, data = null, options = {}) {
|
|
41428
|
+
return this.request(url, 'PATCH', data, options);
|
|
41429
|
+
}
|
|
41430
|
+
async request(url, method, data, options = {}) {
|
|
41431
|
+
// Use native fetch in Node 18+ or require a polyfill for older versions
|
|
41432
|
+
const fetchImpl = globalThis.fetch || require('node-fetch');
|
|
41433
|
+
const headers = {
|
|
41434
|
+
'Content-Type': 'application/json',
|
|
41435
|
+
'User-Agent': 'PERS-SDK-Node/1.0',
|
|
41436
|
+
...options.headers
|
|
41437
|
+
};
|
|
41438
|
+
const fetchOptions = {
|
|
41439
|
+
method,
|
|
41440
|
+
headers,
|
|
41441
|
+
...options
|
|
41442
|
+
};
|
|
41443
|
+
if (data !== undefined && method !== 'GET') {
|
|
41444
|
+
fetchOptions.body = JSON.stringify(data);
|
|
41445
|
+
}
|
|
41446
|
+
const response = await fetchImpl(url, fetchOptions);
|
|
41447
|
+
if (!response.ok) {
|
|
41448
|
+
// Extract backend error response if available
|
|
41449
|
+
let errorData;
|
|
41450
|
+
try {
|
|
41451
|
+
const contentType = response.headers.get('content-type');
|
|
41452
|
+
if (contentType?.includes('application/json')) {
|
|
41453
|
+
errorData = await response.json();
|
|
41454
|
+
}
|
|
41455
|
+
}
|
|
41456
|
+
catch {
|
|
41457
|
+
// If parsing fails, continue with basic error
|
|
41458
|
+
}
|
|
41459
|
+
// Create error with backend data attached
|
|
41460
|
+
const error = new Error(errorData?.message || `HTTP ${response.status}: ${response.statusText}`);
|
|
41461
|
+
error.status = response.status;
|
|
41462
|
+
error.statusText = response.statusText;
|
|
41463
|
+
// Attach backend error response for extraction
|
|
41464
|
+
if (errorData) {
|
|
41465
|
+
Object.assign(error, errorData);
|
|
41466
|
+
}
|
|
41467
|
+
throw error;
|
|
41468
|
+
}
|
|
41469
|
+
// Handle different response types
|
|
41470
|
+
const contentType = response.headers.get('content-type');
|
|
41471
|
+
if (contentType?.includes('application/json')) {
|
|
41472
|
+
return response.json();
|
|
41473
|
+
}
|
|
41474
|
+
return response.text();
|
|
41475
|
+
}
|
|
41476
|
+
}
|
|
41477
|
+
|
|
41478
|
+
/**
|
|
41479
|
+
* Angular HTTP Client adapter for PERS SDK
|
|
41480
|
+
*
|
|
41481
|
+
* Simple bridge between Angular's HttpClient and PERS SDK's HttpClient interface.
|
|
41482
|
+
* Use this for easy integration with Angular applications without requiring Angular as a dependency.
|
|
41483
|
+
*
|
|
41484
|
+
* @example
|
|
41485
|
+
* ```typescript
|
|
41486
|
+
* import { inject } from '@angular/core';
|
|
41487
|
+
* import { HttpClient } from '@angular/common/http';
|
|
41488
|
+
* import { createPersSDK } from '@explorins/pers-sdk';
|
|
41489
|
+
* import { AngularHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
41490
|
+
*
|
|
41491
|
+
* const sdk = createPersSDK(
|
|
41492
|
+
* new AngularHttpClientAdapter(inject(HttpClient)),
|
|
41493
|
+
* { environment: 'production', apiProjectKey: 'your-key' }
|
|
41494
|
+
* );
|
|
41495
|
+
*
|
|
41496
|
+
* // Use managers directly
|
|
41497
|
+
* await sdk.user.getCurrentUser();
|
|
41498
|
+
* ```
|
|
41499
|
+
*/
|
|
41500
|
+
class AngularHttpClientAdapter {
|
|
41501
|
+
constructor(httpClient) {
|
|
41502
|
+
this.httpClient = httpClient;
|
|
41503
|
+
}
|
|
41504
|
+
async get(url, options = {}) {
|
|
41505
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41506
|
+
const request$ = this.httpClient.get(url, mergedOptions);
|
|
41507
|
+
return this.convertObservableToPromise(request$);
|
|
41508
|
+
}
|
|
41509
|
+
async post(url, data = null, options = {}) {
|
|
41510
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41511
|
+
const request$ = this.httpClient.post(url, data, mergedOptions);
|
|
41512
|
+
return this.convertObservableToPromise(request$);
|
|
41513
|
+
}
|
|
41514
|
+
async put(url, data = null, options = {}) {
|
|
41515
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41516
|
+
const request$ = this.httpClient.put(url, data, mergedOptions);
|
|
41517
|
+
return this.convertObservableToPromise(request$);
|
|
41518
|
+
}
|
|
41519
|
+
async delete(url, options = {}) {
|
|
41520
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41521
|
+
const request$ = this.httpClient.delete(url, mergedOptions);
|
|
41522
|
+
return this.convertObservableToPromise(request$);
|
|
41523
|
+
}
|
|
41524
|
+
async patch(url, data = null, options = {}) {
|
|
41525
|
+
const mergedOptions = this.mergeOptions(options);
|
|
41526
|
+
const request$ = this.httpClient.patch(url, data, mergedOptions);
|
|
41527
|
+
return this.convertObservableToPromise(request$);
|
|
41528
|
+
}
|
|
41529
|
+
async convertObservableToPromise(observable) {
|
|
41530
|
+
// Dynamically import firstValueFrom to avoid build-time dependency
|
|
41531
|
+
const { firstValueFrom } = await Promise.resolve().then(function () { return require('./index-CzLxqePZ.js'); }).then(function (n) { return n.index; });
|
|
41532
|
+
return firstValueFrom(observable);
|
|
41533
|
+
}
|
|
41534
|
+
mergeOptions(options) {
|
|
41535
|
+
// Create headers object without importing HttpHeaders
|
|
41536
|
+
const defaultHeaders = { 'Content-Type': 'application/json' };
|
|
41537
|
+
const headers = { ...defaultHeaders, ...options.headers };
|
|
41538
|
+
return {
|
|
41539
|
+
headers,
|
|
41540
|
+
observe: 'body',
|
|
41541
|
+
...options
|
|
41542
|
+
};
|
|
41543
|
+
}
|
|
41544
|
+
}
|
|
41545
|
+
|
|
41546
|
+
/**
|
|
41547
|
+
* Browser Fetch HTTP Client adapter for PERS SDK
|
|
41548
|
+
*
|
|
41549
|
+
* Simple fetch-based HTTP client for browser environments.
|
|
41550
|
+
* Works with vanilla JavaScript, React, Vue, or any browser-based framework.
|
|
41551
|
+
*
|
|
41552
|
+
* @example
|
|
41553
|
+
* ```typescript
|
|
41554
|
+
* import { createPersSDK } from '@explorins/pers-sdk';
|
|
41555
|
+
* import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
41556
|
+
*
|
|
41557
|
+
* const sdk = createPersSDK(
|
|
41558
|
+
* new BrowserFetchClientAdapter(),
|
|
41559
|
+
* { environment: 'production', apiProjectKey: 'your-key' }
|
|
41560
|
+
* );
|
|
41561
|
+
*
|
|
41562
|
+
* // Use managers directly
|
|
41563
|
+
* await sdk.user.getCurrentUser();
|
|
41564
|
+
* ```
|
|
41565
|
+
*/
|
|
41566
|
+
class BrowserFetchClientAdapter {
|
|
41567
|
+
async get(url, options = {}) {
|
|
41568
|
+
return this.request(url, 'GET', undefined, options);
|
|
41569
|
+
}
|
|
41570
|
+
async post(url, data = null, options = {}) {
|
|
41571
|
+
return this.request(url, 'POST', data, options);
|
|
41572
|
+
}
|
|
41573
|
+
async put(url, data = null, options = {}) {
|
|
41574
|
+
return this.request(url, 'PUT', data, options);
|
|
41575
|
+
}
|
|
41576
|
+
async delete(url, options = {}) {
|
|
41577
|
+
return this.request(url, 'DELETE', undefined, options);
|
|
41578
|
+
}
|
|
41579
|
+
async patch(url, data = null, options = {}) {
|
|
41580
|
+
return this.request(url, 'PATCH', data, options);
|
|
41581
|
+
}
|
|
41582
|
+
async request(url, method, data, options = {}) {
|
|
41583
|
+
const headers = {
|
|
41584
|
+
'Content-Type': 'application/json',
|
|
41585
|
+
...options.headers
|
|
41586
|
+
};
|
|
41587
|
+
const fetchOptions = {
|
|
41588
|
+
method,
|
|
41589
|
+
headers,
|
|
41590
|
+
...options
|
|
41591
|
+
};
|
|
41592
|
+
if (data !== undefined && method !== 'GET') {
|
|
41593
|
+
fetchOptions.body = JSON.stringify(data);
|
|
41594
|
+
}
|
|
41595
|
+
const response = await fetch(url, fetchOptions);
|
|
41596
|
+
if (!response.ok) {
|
|
41597
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
41598
|
+
}
|
|
41599
|
+
// Handle different response types
|
|
41600
|
+
const contentType = response.headers.get('content-type');
|
|
41601
|
+
if (contentType?.includes('application/json')) {
|
|
41602
|
+
return response.json();
|
|
41603
|
+
}
|
|
41604
|
+
return response.text();
|
|
41605
|
+
}
|
|
41606
|
+
}
|
|
41607
|
+
|
|
40194
41608
|
/**
|
|
40195
41609
|
* React hook for loading and managing token balances
|
|
40196
41610
|
*
|
|
@@ -40479,15 +41893,41 @@ const SigningStatus = {
|
|
|
40479
41893
|
EXPIRED: 'expired'
|
|
40480
41894
|
};
|
|
40481
41895
|
|
|
40482
|
-
//
|
|
40483
|
-
let
|
|
40484
|
-
|
|
40485
|
-
|
|
40486
|
-
|
|
40487
|
-
|
|
40488
|
-
|
|
40489
|
-
|
|
40490
|
-
|
|
41896
|
+
// Lazy-loaded signer SDK factory - initialized on first use to avoid native module timing issues
|
|
41897
|
+
let _createPersSignerSDK = null;
|
|
41898
|
+
let _signerInitAttempted = false;
|
|
41899
|
+
/**
|
|
41900
|
+
* Lazily load the PERS Signer SDK.
|
|
41901
|
+
*
|
|
41902
|
+
* Supports customer pre-initialization via global.__PersSigner for environments
|
|
41903
|
+
* where native module timing is unpredictable (e.g., certain React Native setups).
|
|
41904
|
+
*
|
|
41905
|
+
* @returns The createPersSignerSDK function or null if unavailable
|
|
41906
|
+
*/
|
|
41907
|
+
function getSignerSDKFactory() {
|
|
41908
|
+
if (_signerInitAttempted) {
|
|
41909
|
+
// Already tried - return cached result (could be null if failed)
|
|
41910
|
+
return _createPersSignerSDK;
|
|
41911
|
+
}
|
|
41912
|
+
_signerInitAttempted = true;
|
|
41913
|
+
// Check for customer pre-initialized global (for native module timing issues)
|
|
41914
|
+
if (typeof global !== 'undefined' && global.__PersSigner?.createPersSignerSDK) {
|
|
41915
|
+
_createPersSignerSDK = global.__PersSigner.createPersSignerSDK;
|
|
41916
|
+
return _createPersSignerSDK;
|
|
41917
|
+
}
|
|
41918
|
+
// Standard lazy load
|
|
41919
|
+
try {
|
|
41920
|
+
const signerModule = require('@explorins/pers-signer/react-native');
|
|
41921
|
+
_createPersSignerSDK = signerModule.createPersSignerSDK;
|
|
41922
|
+
return _createPersSignerSDK;
|
|
41923
|
+
}
|
|
41924
|
+
catch (error) {
|
|
41925
|
+
console.warn('[useTransactionSigner] PERS Signer SDK not available:', error.message);
|
|
41926
|
+
console.warn('[useTransactionSigner] Real blockchain signing will not be available');
|
|
41927
|
+
console.warn('[useTransactionSigner] Tip: If timing issues, pre-initialize in index.js: global.__PersSigner = require("@explorins/pers-signer/react-native")');
|
|
41928
|
+
_createPersSignerSDK = null; // Mark as unavailable
|
|
41929
|
+
return null;
|
|
41930
|
+
}
|
|
40491
41931
|
}
|
|
40492
41932
|
// Constants - TODO: Move to environment config later
|
|
40493
41933
|
const DEFAULT_ETHERS_PROVIDER = "https://sepolia.infura.io/v3/2781b4b5242343d5b0954c98f287b29e";
|
|
@@ -40677,6 +42117,7 @@ const useTransactionSigner = () => {
|
|
|
40677
42117
|
// Auto-initialize signer SDK when PERS SDK is ready
|
|
40678
42118
|
react.useEffect(() => {
|
|
40679
42119
|
const initializeSignerSDK = async () => {
|
|
42120
|
+
const createPersSignerSDK = getSignerSDKFactory();
|
|
40680
42121
|
if (!createPersSignerSDK) {
|
|
40681
42122
|
console.warn('[useTransactionSigner] Signer SDK not available');
|
|
40682
42123
|
return;
|
|
@@ -40791,7 +42232,7 @@ const useTransactionSigner = () => {
|
|
|
40791
42232
|
if (!isSignerInitialized || !signerSDKRef.current) {
|
|
40792
42233
|
throw new Error('Transaction signer not initialized');
|
|
40793
42234
|
}
|
|
40794
|
-
if (!
|
|
42235
|
+
if (!getSignerSDKFactory()) {
|
|
40795
42236
|
throw new Error('PERS Signer SDK not available. Blockchain signing is not supported.');
|
|
40796
42237
|
}
|
|
40797
42238
|
// Create status callback wrapper that updates both hook state and calls user callback
|
|
@@ -40935,7 +42376,7 @@ const useTransactionSigner = () => {
|
|
|
40935
42376
|
* );
|
|
40936
42377
|
* ```
|
|
40937
42378
|
*/
|
|
40938
|
-
isSignerAvailable: isSignerInitialized && !!
|
|
42379
|
+
isSignerAvailable: isSignerInitialized && !!getSignerSDKFactory(),
|
|
40939
42380
|
/**
|
|
40940
42381
|
* Current signing status for UI feedback
|
|
40941
42382
|
*
|
|
@@ -41327,12 +42768,38 @@ const useBusiness = () => {
|
|
|
41327
42768
|
throw error;
|
|
41328
42769
|
}
|
|
41329
42770
|
}, [sdk, isInitialized]);
|
|
41330
|
-
|
|
42771
|
+
/**
|
|
42772
|
+
* Admin: Get all businesses with optional filtering
|
|
42773
|
+
*
|
|
42774
|
+
* @param options - Filter and pagination options
|
|
42775
|
+
* @returns Promise resolving to paginated businesses
|
|
42776
|
+
*
|
|
42777
|
+
* @example
|
|
42778
|
+
* ```typescript
|
|
42779
|
+
* // Get all businesses
|
|
42780
|
+
* const { data } = await getBusinesses();
|
|
42781
|
+
*
|
|
42782
|
+
* // Filter by tag
|
|
42783
|
+
* const tagged = await getBusinesses({ tag: 'restaurant' });
|
|
42784
|
+
*
|
|
42785
|
+
* // Multiple tags (OR match)
|
|
42786
|
+
* const multiTag = await getBusinesses({ tag: ['restaurant', 'cafe'] });
|
|
42787
|
+
*
|
|
42788
|
+
* // With search and pagination
|
|
42789
|
+
* const filtered = await getBusinesses({
|
|
42790
|
+
* search: 'coffee',
|
|
42791
|
+
* active: true,
|
|
42792
|
+
* page: 1,
|
|
42793
|
+
* limit: 20
|
|
42794
|
+
* });
|
|
42795
|
+
* ```
|
|
42796
|
+
*/
|
|
42797
|
+
const getBusinesses = react.useCallback(async (options) => {
|
|
41331
42798
|
if (!isInitialized || !sdk) {
|
|
41332
42799
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
41333
42800
|
}
|
|
41334
42801
|
try {
|
|
41335
|
-
const result = await sdk.businesses.getBusinesses();
|
|
42802
|
+
const result = await sdk.businesses.getBusinesses(options);
|
|
41336
42803
|
return result;
|
|
41337
42804
|
}
|
|
41338
42805
|
catch (error) {
|
|
@@ -41454,11 +42921,15 @@ const useCampaigns = () => {
|
|
|
41454
42921
|
* include: ['triggerSources', 'businesses']
|
|
41455
42922
|
* });
|
|
41456
42923
|
*
|
|
41457
|
-
* // Filter by
|
|
41458
|
-
* const { data } = await getActiveCampaigns({
|
|
42924
|
+
* // Filter by date range
|
|
42925
|
+
* const { data } = await getActiveCampaigns({
|
|
42926
|
+
* startDate: '2026-05-01',
|
|
42927
|
+
* endDate: '2026-12-31'
|
|
42928
|
+
* });
|
|
41459
42929
|
*
|
|
41460
|
-
* //
|
|
41461
|
-
* const { data } = await getActiveCampaigns({
|
|
42930
|
+
* // Get campaigns with claim count
|
|
42931
|
+
* const { data } = await getActiveCampaigns({ include: ['claimCount'] });
|
|
42932
|
+
* data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
|
|
41462
42933
|
* ```
|
|
41463
42934
|
*/
|
|
41464
42935
|
const getActiveCampaigns = react.useCallback(async (options) => {
|
|
@@ -41481,7 +42952,7 @@ const useCampaigns = () => {
|
|
|
41481
42952
|
* Get campaign by ID with optional include relations
|
|
41482
42953
|
*
|
|
41483
42954
|
* @param campaignId - The campaign ID
|
|
41484
|
-
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
42955
|
+
* @param include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
|
|
41485
42956
|
* @returns Promise resolving to campaign data
|
|
41486
42957
|
*
|
|
41487
42958
|
* @example
|
|
@@ -41489,6 +42960,10 @@ const useCampaigns = () => {
|
|
|
41489
42960
|
* // Get campaign with all related data
|
|
41490
42961
|
* const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
|
|
41491
42962
|
* console.log('Trigger sources:', campaign.included?.triggerSources);
|
|
42963
|
+
*
|
|
42964
|
+
* // Get campaign with claim count
|
|
42965
|
+
* const campaign = await getCampaignById('campaign-123', ['claimCount']);
|
|
42966
|
+
* console.log('Total claims:', campaign.claimCount);
|
|
41492
42967
|
* ```
|
|
41493
42968
|
*/
|
|
41494
42969
|
const getCampaignById = react.useCallback(async (campaignId, include) => {
|
|
@@ -41640,8 +43115,13 @@ const useCampaigns = () => {
|
|
|
41640
43115
|
* // Get only active campaigns with trigger sources
|
|
41641
43116
|
* const { data } = await getAllCampaigns({ active: true, include: ['triggerSources'] });
|
|
41642
43117
|
*
|
|
41643
|
-
* //
|
|
41644
|
-
* const { data } = await getAllCampaigns({
|
|
43118
|
+
* // Filter by date range with claim count
|
|
43119
|
+
* const { data } = await getAllCampaigns({
|
|
43120
|
+
* startDate: '2026-05-01',
|
|
43121
|
+
* endDate: '2026-12-31',
|
|
43122
|
+
* include: ['claimCount']
|
|
43123
|
+
* });
|
|
43124
|
+
* data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
|
|
41645
43125
|
*
|
|
41646
43126
|
* // With pagination and sorting
|
|
41647
43127
|
* const { data } = await getAllCampaigns({
|
|
@@ -41668,7 +43148,16 @@ const useCampaigns = () => {
|
|
|
41668
43148
|
/**
|
|
41669
43149
|
* Admin: Get campaign claims with optional filters and include relations
|
|
41670
43150
|
*
|
|
41671
|
-
* @param filters - Optional filters for campaign, user,
|
|
43151
|
+
* @param filters - Optional filters for campaign, user, business, and date range
|
|
43152
|
+
* @param filters.campaignId - Filter by specific campaign ID
|
|
43153
|
+
* @param filters.userId - Filter by user ID
|
|
43154
|
+
* @param filters.businessId - Filter by business ID
|
|
43155
|
+
* @param filters.dateFrom - Filter claims created on or after this date (ISO 8601 format)
|
|
43156
|
+
* @param filters.dateTo - Filter claims created on or before this date (ISO 8601 format)
|
|
43157
|
+
* @param filters.page - Page number (1-indexed)
|
|
43158
|
+
* @param filters.limit - Items per page
|
|
43159
|
+
* @param filters.sortBy - Field to sort by
|
|
43160
|
+
* @param filters.sortOrder - Sort direction
|
|
41672
43161
|
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
41673
43162
|
* @returns Promise resolving to paginated campaign claims
|
|
41674
43163
|
*
|
|
@@ -41679,6 +43168,12 @@ const useCampaigns = () => {
|
|
|
41679
43168
|
*
|
|
41680
43169
|
* // Get claims for a specific campaign
|
|
41681
43170
|
* const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
|
|
43171
|
+
*
|
|
43172
|
+
* // Filter by date range
|
|
43173
|
+
* const { data } = await getCampaignClaims({
|
|
43174
|
+
* dateFrom: '2026-01-01',
|
|
43175
|
+
* dateTo: '2026-03-31'
|
|
43176
|
+
* }, ['campaign']);
|
|
41682
43177
|
* ```
|
|
41683
43178
|
*/
|
|
41684
43179
|
const getCampaignClaims = react.useCallback(async (filters, include) => {
|
|
@@ -41830,6 +43325,47 @@ const useRedemptions = () => {
|
|
|
41830
43325
|
* **Administrators:** Can see all redemptions and filter by active/inactive status
|
|
41831
43326
|
*
|
|
41832
43327
|
* The backend enforces permission-based filtering automatically.
|
|
43328
|
+
*
|
|
43329
|
+
* @param options - Filter and pagination options
|
|
43330
|
+
* @param options.active - Filter by active status (Admin only)
|
|
43331
|
+
* @param options.search - Free text search across name, description, tags
|
|
43332
|
+
* @param options.startDate - Filter redemptions starting on or after this date
|
|
43333
|
+
* @param options.endDate - Filter redemptions ending on or before this date
|
|
43334
|
+
* @param options.include - Relations to include: 'redeemCount'
|
|
43335
|
+
* @param options.page - Page number (1-indexed)
|
|
43336
|
+
* @param options.limit - Results per page
|
|
43337
|
+
* @param options.sortBy - Field to sort by ('name' | 'createdAt')
|
|
43338
|
+
* @param options.sortOrder - Sort direction ('ASC' | 'DESC')
|
|
43339
|
+
*
|
|
43340
|
+
* @example Basic usage
|
|
43341
|
+
* ```typescript
|
|
43342
|
+
* const { data: offers } = await getRedemptions();
|
|
43343
|
+
* ```
|
|
43344
|
+
*
|
|
43345
|
+
* @example Filter by date range
|
|
43346
|
+
* ```typescript
|
|
43347
|
+
* const { data: dateFiltered } = await getRedemptions({
|
|
43348
|
+
* startDate: '2026-05-01',
|
|
43349
|
+
* endDate: '2026-06-30'
|
|
43350
|
+
* });
|
|
43351
|
+
* ```
|
|
43352
|
+
*
|
|
43353
|
+
* @example Get redemptions with redeem count
|
|
43354
|
+
* ```typescript
|
|
43355
|
+
* const { data: offers } = await getRedemptions({ include: ['redeemCount'] });
|
|
43356
|
+
* offers.forEach(o => console.log(`${o.name}: ${o.redeemCount} redeems`));
|
|
43357
|
+
* ```
|
|
43358
|
+
*
|
|
43359
|
+
* @example Search with pagination
|
|
43360
|
+
* ```typescript
|
|
43361
|
+
* const { data, pagination } = await getRedemptions({
|
|
43362
|
+
* search: 'discount',
|
|
43363
|
+
* page: 1,
|
|
43364
|
+
* limit: 10,
|
|
43365
|
+
* sortBy: 'name',
|
|
43366
|
+
* sortOrder: 'ASC'
|
|
43367
|
+
* });
|
|
43368
|
+
* ```
|
|
41833
43369
|
*/
|
|
41834
43370
|
const getRedemptions = react.useCallback(async (options) => {
|
|
41835
43371
|
if (!isInitialized || !sdk) {
|
|
@@ -41985,7 +43521,17 @@ const useRedemptions = () => {
|
|
|
41985
43521
|
*
|
|
41986
43522
|
* Retrieves all redemption redeems across the platform with filtering.
|
|
41987
43523
|
*
|
|
41988
|
-
* @param filters - Optional filters for user, redemption, and pagination
|
|
43524
|
+
* @param filters - Optional filters for user, redemption, date range, and pagination
|
|
43525
|
+
* @param filters.userId - Filter by user ID
|
|
43526
|
+
* @param filters.redemptionId - Filter by redemption offer ID
|
|
43527
|
+
* @param filters.businessId - Filter by business ID (admin only)
|
|
43528
|
+
* @param filters.status - Filter by processing status (PENDING, PROCESSING, COMPLETED, FAILED)
|
|
43529
|
+
* @param filters.dateFrom - Filter redeems created on or after this date (ISO 8601 format)
|
|
43530
|
+
* @param filters.dateTo - Filter redeems created on or before this date (ISO 8601 format)
|
|
43531
|
+
* @param filters.page - Page number (1-indexed)
|
|
43532
|
+
* @param filters.limit - Items per page
|
|
43533
|
+
* @param filters.sortBy - Field to sort by
|
|
43534
|
+
* @param filters.sortOrder - Sort direction
|
|
41989
43535
|
* @param include - Relations to include: 'redemption', 'user', 'business'
|
|
41990
43536
|
* @returns Promise resolving to paginated redemption redeems
|
|
41991
43537
|
*
|
|
@@ -41999,6 +43545,12 @@ const useRedemptions = () => {
|
|
|
41999
43545
|
* { userId: 'user-123' },
|
|
42000
43546
|
* ['redemption']
|
|
42001
43547
|
* );
|
|
43548
|
+
*
|
|
43549
|
+
* // Filter by date range
|
|
43550
|
+
* const { data } = await getRedemptionRedeems({
|
|
43551
|
+
* dateFrom: '2026-01-01',
|
|
43552
|
+
* dateTo: '2026-03-31'
|
|
43553
|
+
* }, ['user', 'redemption']);
|
|
42002
43554
|
* ```
|
|
42003
43555
|
*/
|
|
42004
43556
|
const getRedemptionRedeems = react.useCallback(async (filters, include) => {
|
|
@@ -43481,112 +45033,125 @@ const useTriggerSources = () => {
|
|
|
43481
45033
|
};
|
|
43482
45034
|
};
|
|
43483
45035
|
|
|
43484
|
-
// ==========================================
|
|
43485
|
-
// TOKEN UTILITY FUNCTIONS
|
|
43486
|
-
// ==========================================
|
|
43487
|
-
/**
|
|
43488
|
-
* Get metadata from a token unit response
|
|
43489
|
-
* Handles metadata-based tokens (ERC-1155 with metadata array)
|
|
43490
|
-
*
|
|
43491
|
-
* For metadata-based tokens:
|
|
43492
|
-
* - Returns the metadata object from token.metadata array
|
|
43493
|
-
* - Uses provided incrementalId or tokenMetadataIncrementalId from tokenUnit
|
|
43494
|
-
* - Defaults to index 0 if no incrementalId specified
|
|
43495
|
-
*
|
|
43496
|
-
* For standard tokens (no metadata):
|
|
43497
|
-
* - Returns null
|
|
43498
|
-
*
|
|
43499
|
-
* @param tokenUnit - The token unit from campaign or balance
|
|
43500
|
-
* @param incrementalId - Optional metadata index to use (overrides tokenUnit.tokenMetadataIncrementalId)
|
|
43501
|
-
* @returns Token metadata object or null
|
|
43502
|
-
*
|
|
43503
|
-
* @example
|
|
43504
|
-
* ```typescript
|
|
43505
|
-
* // Get metadata using tokenUnit's incrementalId
|
|
43506
|
-
* const tokenUnit = {
|
|
43507
|
-
* tokenMetadataIncrementalId: 3,
|
|
43508
|
-
* token: {
|
|
43509
|
-
* metadata: [
|
|
43510
|
-
* { name: 'Bronze Pass', imageUrl: '...' },
|
|
43511
|
-
* { name: 'Silver Pass', imageUrl: '...' },
|
|
43512
|
-
* { name: 'Gold Pass', imageUrl: '...' }
|
|
43513
|
-
* ]
|
|
43514
|
-
* }
|
|
43515
|
-
* };
|
|
43516
|
-
* const metadata = getMetadataFromTokenUnitResponse(tokenUnit);
|
|
43517
|
-
* // Returns: { name: 'Bronze Pass', imageUrl: '...' } (index 0 - default)
|
|
43518
|
-
*
|
|
43519
|
-
* // Override incrementalId
|
|
43520
|
-
* const metadata2 = getMetadataFromTokenUnitResponse(tokenUnit, 2);
|
|
43521
|
-
* // Returns: { name: 'Gold Pass', imageUrl: '...' } (index 2)
|
|
43522
|
-
*
|
|
43523
|
-
* // Standard token (no metadata)
|
|
43524
|
-
* const tokenUnit2 = {
|
|
43525
|
-
* token: { symbol: 'VQP', name: 'Visit Qatar Points' }
|
|
43526
|
-
* };
|
|
43527
|
-
* const metadata3 = getMetadataFromTokenUnitResponse(tokenUnit2);
|
|
43528
|
-
* // Returns: null
|
|
43529
|
-
*
|
|
43530
|
-
* // Display name with fallback
|
|
43531
|
-
* const displayName = metadata?.name || tokenUnit.token?.symbol || 'Reward';
|
|
43532
|
-
*
|
|
43533
|
-
* // Display image
|
|
43534
|
-
* const imageUrl = metadata?.imageUrl;
|
|
43535
|
-
* ```
|
|
43536
|
-
*/
|
|
43537
|
-
function getMetadataFromTokenUnitResponse(tokenUnit, incrementalId) {
|
|
43538
|
-
// If no metadata array exists, return null
|
|
43539
|
-
if (!tokenUnit.token?.metadata || tokenUnit.token.metadata.length === 0) {
|
|
43540
|
-
return null;
|
|
43541
|
-
}
|
|
43542
|
-
const metadata = tokenUnit.token.metadata;
|
|
43543
|
-
// Use provided incrementalId or tokenUnit's incrementalId to find matching metadata
|
|
43544
|
-
const targetId = incrementalId ?? tokenUnit.tokenMetadataIncrementalId;
|
|
43545
|
-
if (targetId !== undefined) {
|
|
43546
|
-
const found = metadata.find((m) => m.incrementalId === targetId);
|
|
43547
|
-
if (found)
|
|
43548
|
-
return found;
|
|
43549
|
-
}
|
|
43550
|
-
// Fallback to first metadata item
|
|
43551
|
-
return metadata[0];
|
|
43552
|
-
}
|
|
43553
|
-
|
|
43554
45036
|
exports.ANALYTICS_GROUP_BY_TYPES = ANALYTICS_GROUP_BY_TYPES;
|
|
43555
45037
|
exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
|
|
45038
|
+
exports.AUTH_STORAGE_KEYS = AUTH_STORAGE_KEYS;
|
|
45039
|
+
exports.AnalyticsApi = AnalyticsApi;
|
|
45040
|
+
exports.AnalyticsManager = AnalyticsManager;
|
|
45041
|
+
exports.AnalyticsService = AnalyticsService;
|
|
45042
|
+
exports.AngularHttpClientAdapter = AngularHttpClientAdapter;
|
|
45043
|
+
exports.ApiErrorDetector = ApiErrorDetector;
|
|
45044
|
+
exports.ApiKeyApi = ApiKeyApi;
|
|
45045
|
+
exports.ApiKeyManager = ApiKeyManager;
|
|
43556
45046
|
exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
|
|
43557
45047
|
exports.AuditEntityTypes = AuditEntityTypes;
|
|
45048
|
+
exports.AuthApi = AuthApi;
|
|
45049
|
+
exports.AuthManager = AuthManager;
|
|
45050
|
+
exports.AuthService = AuthService;
|
|
45051
|
+
exports.AuthTokenManager = AuthTokenManager;
|
|
43558
45052
|
exports.AuthenticationError = AuthenticationError;
|
|
45053
|
+
exports.BaseTokenService = BaseTokenService;
|
|
45054
|
+
exports.BrowserFetchClientAdapter = BrowserFetchClientAdapter;
|
|
45055
|
+
exports.BusinessApi = BusinessApi;
|
|
45056
|
+
exports.BusinessManager = BusinessManager;
|
|
45057
|
+
exports.BusinessMembershipApi = BusinessMembershipApi;
|
|
45058
|
+
exports.BusinessMembershipService = BusinessMembershipService;
|
|
45059
|
+
exports.BusinessService = BusinessService;
|
|
45060
|
+
exports.CacheService = CacheService;
|
|
45061
|
+
exports.CacheTTL = CacheTTL;
|
|
45062
|
+
exports.CampaignApi = CampaignApi;
|
|
45063
|
+
exports.CampaignManager = CampaignManager;
|
|
45064
|
+
exports.CampaignService = CampaignService;
|
|
43559
45065
|
exports.ChainTypes = ChainTypes;
|
|
43560
45066
|
exports.ClientTransactionType = ClientTransactionType;
|
|
45067
|
+
exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
|
|
45068
|
+
exports.DPOP_STORAGE_KEYS = DPOP_STORAGE_KEYS;
|
|
45069
|
+
exports.DPoPManager = DPoPManager;
|
|
45070
|
+
exports.DefaultAuthProvider = DefaultAuthProvider;
|
|
43561
45071
|
exports.Domains = Domains;
|
|
45072
|
+
exports.DonationApi = DonationApi;
|
|
45073
|
+
exports.DonationManager = DonationManager;
|
|
45074
|
+
exports.DonationService = DonationService;
|
|
43562
45075
|
exports.EntityTypes = EntityTypes;
|
|
43563
45076
|
exports.ErrorDomains = ErrorDomains;
|
|
43564
45077
|
exports.ErrorUtils = ErrorUtils;
|
|
45078
|
+
exports.FileApi = FileApi;
|
|
45079
|
+
exports.FileManager = FileManager;
|
|
45080
|
+
exports.FileService = FileService;
|
|
45081
|
+
exports.IndexedDBTokenStorage = IndexedDBTokenStorage;
|
|
45082
|
+
exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
|
|
45083
|
+
exports.LogoutRequired = LogoutRequired;
|
|
43565
45084
|
exports.MEMBERSHIP_ROLE_HIERARCHY = MEMBERSHIP_ROLE_HIERARCHY;
|
|
45085
|
+
exports.MemoryTokenStorage = MemoryTokenStorage;
|
|
43566
45086
|
exports.NativeTokenTypes = NativeTokenTypes;
|
|
45087
|
+
exports.NetworkError = NetworkError;
|
|
45088
|
+
exports.NodeHttpClientAdapter = NodeHttpClientAdapter;
|
|
45089
|
+
exports.PaymentApi = PurchaseApi;
|
|
45090
|
+
exports.PaymentService = PaymentService;
|
|
45091
|
+
exports.PersApiClient = PersApiClient;
|
|
43567
45092
|
exports.PersApiError = PersApiError;
|
|
45093
|
+
exports.PersEventEmitter = PersEventEmitter;
|
|
45094
|
+
exports.PersEventsClient = PersEventsClient;
|
|
45095
|
+
exports.PersSDK = PersSDK;
|
|
43568
45096
|
exports.PersSDKProvider = PersSDKProvider;
|
|
45097
|
+
exports.ProviderTokenRefreshNeeded = ProviderTokenRefreshNeeded;
|
|
45098
|
+
exports.PurchaseManager = PurchaseManager;
|
|
43569
45099
|
exports.QUERY_OPERATOR_SQL_MAP = QUERY_OPERATOR_SQL_MAP;
|
|
43570
45100
|
exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
|
|
43571
45101
|
exports.ReactNativeHttpClient = ReactNativeHttpClient;
|
|
43572
45102
|
exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
|
|
45103
|
+
exports.RedemptionApi = RedemptionApi;
|
|
45104
|
+
exports.RedemptionManager = RedemptionManager;
|
|
45105
|
+
exports.RedemptionService = RedemptionService;
|
|
45106
|
+
exports.SDK_NAME = SDK_NAME;
|
|
45107
|
+
exports.SDK_USER_AGENT = SDK_USER_AGENT;
|
|
45108
|
+
exports.SDK_VERSION = SDK_VERSION;
|
|
43573
45109
|
exports.SOURCE_LOGIC_TYPES = SOURCE_LOGIC_TYPES;
|
|
43574
45110
|
exports.SOURCE_LOGIC_TYPE_VALUES = SOURCE_LOGIC_TYPE_VALUES;
|
|
45111
|
+
exports.SdkErrorCodes = SdkErrorCodes;
|
|
43575
45112
|
exports.SigningStatus = SigningStatus;
|
|
45113
|
+
exports.StaticJwtAuthProvider = StaticJwtAuthProvider;
|
|
43576
45114
|
exports.TOKEN_VALIDITY_TYPE_VALUES = TOKEN_VALIDITY_TYPE_VALUES;
|
|
43577
45115
|
exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
|
|
43578
45116
|
exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
|
|
43579
45117
|
exports.TRIGGER_SOURCE_TYPES = TRIGGER_SOURCE_TYPES;
|
|
43580
45118
|
exports.TRIGGER_SOURCE_TYPE_VALUES = TRIGGER_SOURCE_TYPE_VALUES;
|
|
45119
|
+
exports.TenantApi = TenantApi;
|
|
45120
|
+
exports.TenantManager = TenantManager;
|
|
45121
|
+
exports.TenantService = TenantService;
|
|
45122
|
+
exports.TokenApi = TokenApi;
|
|
45123
|
+
exports.TokenManager = TokenManager;
|
|
45124
|
+
exports.TokenRefreshNeeded = TokenRefreshNeeded;
|
|
45125
|
+
exports.TokenSDK = TokenSDK;
|
|
45126
|
+
exports.TokenService = TokenService;
|
|
43581
45127
|
exports.TokenValidityTypes = TokenValidityTypes;
|
|
45128
|
+
exports.TransactionApi = TransactionApi;
|
|
45129
|
+
exports.TransactionManager = TransactionManager;
|
|
45130
|
+
exports.TransactionService = TransactionService;
|
|
45131
|
+
exports.TriggerSourceApi = TriggerSourceApi;
|
|
45132
|
+
exports.TriggerSourceManager = TriggerSourceManager;
|
|
45133
|
+
exports.TriggerSourceService = TriggerSourceService;
|
|
45134
|
+
exports.UserApi = UserApi;
|
|
45135
|
+
exports.UserManager = UserManager;
|
|
45136
|
+
exports.UserService = UserService;
|
|
45137
|
+
exports.UserStatusApi = UserStatusApi;
|
|
45138
|
+
exports.UserStatusManager = UserStatusManager;
|
|
45139
|
+
exports.UserStatusService = UserStatusService;
|
|
43582
45140
|
exports.VALID_BUSINESS_MEMBERSHIP_RELATIONS = VALID_BUSINESS_MEMBERSHIP_RELATIONS;
|
|
43583
45141
|
exports.VALID_CAMPAIGN_CLAIM_RELATIONS = VALID_CAMPAIGN_CLAIM_RELATIONS;
|
|
43584
45142
|
exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
|
|
43585
45143
|
exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
|
|
45144
|
+
exports.VALID_REDEMPTION_RELATIONS = VALID_REDEMPTION_RELATIONS;
|
|
43586
45145
|
exports.VALID_RELATIONS = VALID_RELATIONS;
|
|
43587
45146
|
exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
|
|
43588
45147
|
exports.VALID_USER_RELATIONS = VALID_USER_RELATIONS;
|
|
45148
|
+
exports.WalletEventsManager = WalletEventsManager;
|
|
45149
|
+
exports.WebDPoPCryptoProvider = WebDPoPCryptoProvider;
|
|
45150
|
+
exports.WebhookApi = WebhookApi;
|
|
45151
|
+
exports.WebhookManager = WebhookManager;
|
|
45152
|
+
exports.WebhookService = WebhookService;
|
|
43589
45153
|
exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;
|
|
45154
|
+
exports.buildApiRoot = buildApiRoot;
|
|
43590
45155
|
exports.buildBurnRequest = buildBurnRequest;
|
|
43591
45156
|
exports.buildMintRequest = buildMintRequest;
|
|
43592
45157
|
exports.buildPOSBurnRequest = buildPOSBurnRequest;
|
|
@@ -43595,14 +45160,22 @@ exports.buildPaginationParams = buildPaginationParams;
|
|
|
43595
45160
|
exports.buildPendingTransactionData = buildPendingTransactionData;
|
|
43596
45161
|
exports.buildSubmissionRequest = buildSubmissionRequest;
|
|
43597
45162
|
exports.buildTransferRequest = buildTransferRequest;
|
|
45163
|
+
exports.buildWalletEventsWsUrl = buildWalletEventsWsUrl;
|
|
45164
|
+
exports.createPersEventsClient = createPersEventsClient;
|
|
45165
|
+
exports.createPersSDK = createPersSDK;
|
|
43598
45166
|
exports.createReactNativeAuthProvider = createReactNativeAuthProvider;
|
|
43599
45167
|
exports.createUserIdentifierObject = createUserIdentifierObject;
|
|
45168
|
+
exports.createUserStatusSDK = createUserStatusSDK;
|
|
45169
|
+
exports.detectEnvironment = detectEnvironment;
|
|
43600
45170
|
exports.detectIdentifierType = detectIdentifierType;
|
|
45171
|
+
exports.environment = environment;
|
|
43601
45172
|
exports.extractData = extractData;
|
|
43602
45173
|
exports.extractDeadlineFromSigningData = extractDeadlineFromSigningData;
|
|
43603
45174
|
exports.extractPagination = extractPagination;
|
|
43604
45175
|
exports.fetchAllPages = fetchAllPages;
|
|
43605
45176
|
exports.getMetadataFromTokenUnitResponse = getMetadataFromTokenUnitResponse;
|
|
45177
|
+
exports.getSigningUrl = getSigningUrl;
|
|
45178
|
+
exports.globalCacheService = globalCacheService;
|
|
43606
45179
|
exports.hasMinimumRole = hasMinimumRole;
|
|
43607
45180
|
exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
|
|
43608
45181
|
exports.isConnectedMessage = isConnectedMessage;
|
|
@@ -43618,9 +45191,14 @@ exports.isValidBusinessMembershipRelation = isValidBusinessMembershipRelation;
|
|
|
43618
45191
|
exports.isValidCampaignClaimRelation = isValidCampaignClaimRelation;
|
|
43619
45192
|
exports.isValidCampaignRelation = isValidCampaignRelation;
|
|
43620
45193
|
exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
|
|
45194
|
+
exports.isValidRedemptionRelation = isValidRedemptionRelation;
|
|
43621
45195
|
exports.isValidRelation = isValidRelation;
|
|
43622
45196
|
exports.isValidTransactionRelation = isValidTransactionRelation;
|
|
43623
45197
|
exports.isValidUserRelation = isValidUserRelation;
|
|
45198
|
+
exports.mergeWithDefaults = mergeWithDefaults;
|
|
45199
|
+
exports.needsExternalSigning = needsExternalSigning;
|
|
45200
|
+
exports.needsSubmission = needsSubmission;
|
|
45201
|
+
exports.normalizeFilters = normalizeFilters;
|
|
43624
45202
|
exports.normalizeToPaginated = normalizeToPaginated;
|
|
43625
45203
|
exports.registerIdentifierType = registerIdentifierType;
|
|
43626
45204
|
exports.testnetPrefix = testnetPrefix;
|
|
@@ -43645,4 +45223,5 @@ exports.useTriggerSources = useTriggerSources;
|
|
|
43645
45223
|
exports.useUserStatus = useUserStatus;
|
|
43646
45224
|
exports.useUsers = useUsers;
|
|
43647
45225
|
exports.useWeb3 = useWeb3;
|
|
45226
|
+
exports.warnIfProblematicEnvironment = warnIfProblematicEnvironment;
|
|
43648
45227
|
//# sourceMappingURL=index.js.map
|