@explorins/pers-sdk-react-native 2.2.0 → 2.2.2
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 +8 -0
- package/dist/hooks/useRedemptions.d.ts +1 -0
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +69 -3
- package/dist/hooks/useTokenBalances.d.ts +35 -2
- package/dist/hooks/useTokenBalances.d.ts.map +1 -1
- package/dist/hooks/useTokens.d.ts +4 -1
- package/dist/hooks/useTokens.d.ts.map +1 -1
- package/dist/hooks/useTokens.js +96 -0
- package/dist/index.js +739 -86
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts +36 -2
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/package.json +199 -199
- package/src/hooks/useRedemptions.ts +73 -3
- package/src/hooks/useTokenBalances.ts +36 -2
- package/src/hooks/useTokens.ts +100 -1
- package/src/providers/PersSDKProvider.tsx +36 -2
- package/dist/__tests__/__mocks__/pers-sdk-core.d.ts +0 -42
- package/dist/__tests__/__mocks__/pers-sdk-core.d.ts.map +0 -1
- package/dist/__tests__/__mocks__/pers-sdk-core.js +0 -48
- package/dist/__tests__/__mocks__/pers-shared.d.ts +0 -15
- package/dist/__tests__/__mocks__/pers-shared.d.ts.map +0 -1
- package/dist/__tests__/__mocks__/pers-shared.js +0 -2
- package/src/__tests__/__mocks__/pers-sdk-core.ts +0 -83
- package/src/__tests__/__mocks__/pers-shared.ts +0 -17
package/dist/index.js
CHANGED
|
@@ -3152,6 +3152,8 @@ exports.WebhookSource = void 0;
|
|
|
3152
3152
|
WebhookSource["USER"] = "USER";
|
|
3153
3153
|
WebhookSource["BUSINESS"] = "BUSINESS";
|
|
3154
3154
|
WebhookSource["TENANT"] = "TENANT";
|
|
3155
|
+
// System-level internal invocation (e.g., token metadata webhook fetch)
|
|
3156
|
+
WebhookSource["INTERNAL"] = "INTERNAL";
|
|
3155
3157
|
// External verified sources
|
|
3156
3158
|
WebhookSource["STRIPE"] = "STRIPE";
|
|
3157
3159
|
// Allow any external traffic (no verification)
|
|
@@ -3216,6 +3218,12 @@ const EntityTypes = {
|
|
|
3216
3218
|
SIGNING_ACCOUNT: 'signing_account',
|
|
3217
3219
|
API_KEY: 'api_key',
|
|
3218
3220
|
WEBHOOK: 'webhook',
|
|
3221
|
+
// Service domains
|
|
3222
|
+
ANALYTICS: 'analytics',
|
|
3223
|
+
CHAIN: 'chain',
|
|
3224
|
+
FILE: 'file',
|
|
3225
|
+
JWKS: 'jwks',
|
|
3226
|
+
PROJECT: 'project',
|
|
3219
3227
|
};
|
|
3220
3228
|
/**
|
|
3221
3229
|
* Error-specific domains (extends EntityTypes)
|
|
@@ -3551,7 +3559,8 @@ function isValidTransactionRelation(value) {
|
|
|
3551
3559
|
const VALID_CAMPAIGN_RELATIONS = [
|
|
3552
3560
|
'triggerSources',
|
|
3553
3561
|
'businesses',
|
|
3554
|
-
'claimCount'
|
|
3562
|
+
'claimCount',
|
|
3563
|
+
'ownerBusiness'
|
|
3555
3564
|
];
|
|
3556
3565
|
/**
|
|
3557
3566
|
* Type guard to validate campaign include relation at runtime
|
|
@@ -3589,7 +3598,8 @@ function isValidCampaignClaimRelation(value) {
|
|
|
3589
3598
|
* Used for runtime validation
|
|
3590
3599
|
*/
|
|
3591
3600
|
const VALID_REDEMPTION_RELATIONS = [
|
|
3592
|
-
'redeemCount'
|
|
3601
|
+
'redeemCount',
|
|
3602
|
+
'ownerBusiness'
|
|
3593
3603
|
];
|
|
3594
3604
|
/**
|
|
3595
3605
|
* Type guard to validate redemption include relation at runtime
|
|
@@ -3647,6 +3657,26 @@ function isValidUserRelation(value) {
|
|
|
3647
3657
|
return VALID_USER_RELATIONS.includes(value);
|
|
3648
3658
|
}
|
|
3649
3659
|
|
|
3660
|
+
/**
|
|
3661
|
+
* Array of all valid token metadata include relations
|
|
3662
|
+
* Used for runtime validation
|
|
3663
|
+
*/
|
|
3664
|
+
const VALID_TOKEN_METADATA_RELATIONS = [
|
|
3665
|
+
'ownerBusiness',
|
|
3666
|
+
'mintCount',
|
|
3667
|
+
'burnCount',
|
|
3668
|
+
'token'
|
|
3669
|
+
];
|
|
3670
|
+
/**
|
|
3671
|
+
* Type guard to validate token metadata include relation at runtime
|
|
3672
|
+
*
|
|
3673
|
+
* @param value - String to validate
|
|
3674
|
+
* @returns True if value is a valid TokenMetadataIncludeRelation
|
|
3675
|
+
*/
|
|
3676
|
+
function isValidTokenMetadataRelation(value) {
|
|
3677
|
+
return VALID_TOKEN_METADATA_RELATIONS.includes(value);
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3650
3680
|
/**
|
|
3651
3681
|
* Normalizes filter options to use new field names while accepting deprecated ones.
|
|
3652
3682
|
* Use at controller level to support both old and new API contracts.
|
|
@@ -3726,6 +3756,9 @@ const CommonErrorCodes = {
|
|
|
3726
3756
|
TOKEN_NOT_ACTIVE: 'TOKEN_NOT_ACTIVE',
|
|
3727
3757
|
TOKEN_ID_REQUIRED: 'TOKEN_ID_REQUIRED',
|
|
3728
3758
|
TOKEN_TYPE_NOT_FOUND: 'TOKEN_TYPE_NOT_FOUND',
|
|
3759
|
+
TOKEN_UNIT_NOT_FOUND: 'TOKEN_UNIT_NOT_FOUND',
|
|
3760
|
+
NFT_EXPIRED: 'NFT_EXPIRED',
|
|
3761
|
+
NFT_BUSINESS_RESTRICTION: 'NFT_BUSINESS_RESTRICTION',
|
|
3729
3762
|
// Wallet domain
|
|
3730
3763
|
WALLET_MISSING_SIGNING: 'WALLET_MISSING_SIGNING',
|
|
3731
3764
|
WALLET_NOT_FOUND: 'WALLET_NOT_FOUND',
|
|
@@ -3765,6 +3798,9 @@ const CommonErrorCodes = {
|
|
|
3765
3798
|
CAMPAIGN_DAILY_LIMIT_REACHED: 'CAMPAIGN_DAILY_LIMIT_REACHED',
|
|
3766
3799
|
CAMPAIGN_GLOBAL_LIMIT_REACHED: 'CAMPAIGN_GLOBAL_LIMIT_REACHED',
|
|
3767
3800
|
CAMPAIGN_GLOBAL_DAILY_LIMIT_REACHED: 'CAMPAIGN_GLOBAL_DAILY_LIMIT_REACHED',
|
|
3801
|
+
CAMPAIGN_NOT_FOUND: 'CAMPAIGN_NOT_FOUND',
|
|
3802
|
+
GEOGRAPHIC_RESTRICTION: 'GEOGRAPHIC_RESTRICTION',
|
|
3803
|
+
LOCATION_VERIFICATION_REQUIRED: 'LOCATION_VERIFICATION_REQUIRED',
|
|
3768
3804
|
// Trigger domain
|
|
3769
3805
|
TRIGGER_NOT_FOUND: 'TRIGGER_NOT_FOUND',
|
|
3770
3806
|
// Trigger Source domain
|
|
@@ -3780,6 +3816,12 @@ const CommonErrorCodes = {
|
|
|
3780
3816
|
EXTERNAL_SERVICE_ERROR: 'EXTERNAL_SERVICE_ERROR',
|
|
3781
3817
|
AUTHENTICATION_REQUIRED: 'AUTHENTICATION_REQUIRED',
|
|
3782
3818
|
AUTHORIZATION_FAILED: 'AUTHORIZATION_FAILED',
|
|
3819
|
+
CONFIGURATION_MISSING: 'CONFIGURATION_MISSING',
|
|
3820
|
+
// Redemption domain
|
|
3821
|
+
USER_STATUS_RESTRICTED: 'USER_STATUS_RESTRICTED',
|
|
3822
|
+
REDEMPTION_ALREADY_PROCESSING: 'REDEMPTION_ALREADY_PROCESSING',
|
|
3823
|
+
MAX_REDEMPTION: 'MAX_REDEMPTION',
|
|
3824
|
+
USER_LIMIT: 'USER_LIMIT',
|
|
3783
3825
|
// Authentication errors
|
|
3784
3826
|
INVALID_TOKEN: 'INVALID_TOKEN',
|
|
3785
3827
|
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
|
@@ -3787,6 +3829,8 @@ const CommonErrorCodes = {
|
|
|
3787
3829
|
REFRESH_TOKEN_EXPIRED: 'REFRESH_TOKEN_EXPIRED',
|
|
3788
3830
|
REFRESH_TOKEN_REVOKED: 'REFRESH_TOKEN_REVOKED',
|
|
3789
3831
|
FIREBASE_AUTH_ERROR: 'FIREBASE_AUTH_ERROR',
|
|
3832
|
+
MULTIPLE_CONTEXT_SELECTION_REQUIRED: 'MULTIPLE_CONTEXT_SELECTION_REQUIRED',
|
|
3833
|
+
ADMIN_TENANT_REQUIRED: 'ADMIN_TENANT_REQUIRED',
|
|
3790
3834
|
// Generic fallbacks
|
|
3791
3835
|
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
3792
3836
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
@@ -3883,12 +3927,12 @@ class ErrorUtils {
|
|
|
3883
3927
|
return false;
|
|
3884
3928
|
const err = error;
|
|
3885
3929
|
// Check for explicit token expired indicators
|
|
3886
|
-
if (err?.code ===
|
|
3930
|
+
if (err?.code === CommonErrorCodes.TOKEN_EXPIRED || err?.errorCode === CommonErrorCodes.TOKEN_EXPIRED) {
|
|
3887
3931
|
return true;
|
|
3888
3932
|
}
|
|
3889
3933
|
// Check in response data
|
|
3890
3934
|
const apiError = err?.response?.data || err;
|
|
3891
|
-
if (apiError?.code ===
|
|
3935
|
+
if (apiError?.code === CommonErrorCodes.TOKEN_EXPIRED || apiError?.errorCode === CommonErrorCodes.TOKEN_EXPIRED) {
|
|
3892
3936
|
return true;
|
|
3893
3937
|
}
|
|
3894
3938
|
// Any 401 error is treated as token expiration
|
|
@@ -3958,7 +4002,9 @@ class ErrorUtils {
|
|
|
3958
4002
|
// Try extraction strategies in priority order
|
|
3959
4003
|
const candidates = [
|
|
3960
4004
|
this.parseHttpClientError(error), // React Native format
|
|
3961
|
-
httpError.response
|
|
4005
|
+
httpError.response && typeof httpError.response === 'object'
|
|
4006
|
+
? httpError.response.data
|
|
4007
|
+
: undefined, // Axios/fetch format
|
|
3962
4008
|
httpError.error, // Angular HttpErrorResponse
|
|
3963
4009
|
httpError // Direct error object
|
|
3964
4010
|
];
|
|
@@ -3977,8 +4023,9 @@ class ErrorUtils {
|
|
|
3977
4023
|
* Check if object has structured error fields from backend (StructuredError)
|
|
3978
4024
|
*/
|
|
3979
4025
|
static hasStructuredErrorFields(obj) {
|
|
3980
|
-
return obj && typeof obj === 'object' &&
|
|
3981
|
-
((
|
|
4026
|
+
return obj !== null && typeof obj === 'object' &&
|
|
4027
|
+
(('code' in obj && 'category' in obj) ||
|
|
4028
|
+
('status' in obj && ('detail' in obj || 'message' in obj || 'title' in obj)));
|
|
3982
4029
|
}
|
|
3983
4030
|
/**
|
|
3984
4031
|
* Create fallback error structure when no structured error found
|
|
@@ -3989,9 +4036,9 @@ class ErrorUtils {
|
|
|
3989
4036
|
title: 'Request failed',
|
|
3990
4037
|
message: this.getMessage(error),
|
|
3991
4038
|
detail: this.getMessage(error),
|
|
3992
|
-
code:
|
|
4039
|
+
code: CommonErrorCodes.TECHNICAL_ERROR,
|
|
3993
4040
|
category: exports.ErrorCategory.TECHNICAL,
|
|
3994
|
-
domain:
|
|
4041
|
+
domain: ErrorDomains.EXTERNAL,
|
|
3995
4042
|
retryable: this.isRetryable(error)
|
|
3996
4043
|
};
|
|
3997
4044
|
}
|
|
@@ -4070,7 +4117,7 @@ class PersApiError extends Error {
|
|
|
4070
4117
|
this.method = method;
|
|
4071
4118
|
this.name = 'PersApiError';
|
|
4072
4119
|
// Use backend error details directly
|
|
4073
|
-
this.code = backendError?.code ||
|
|
4120
|
+
this.code = backendError?.code || CommonErrorCodes.TECHNICAL_ERROR;
|
|
4074
4121
|
this.domain = backendError?.domain || ErrorDomains.EXTERNAL;
|
|
4075
4122
|
this.category = backendError?.category || exports.ErrorCategory.TECHNICAL;
|
|
4076
4123
|
this.retryable = backendError?.retryable ?? (statusCode ? statusCode >= 500 : false);
|
|
@@ -4094,7 +4141,7 @@ class AuthenticationError extends PersApiError {
|
|
|
4094
4141
|
class NetworkError extends Error {
|
|
4095
4142
|
constructor(message) {
|
|
4096
4143
|
super(message);
|
|
4097
|
-
this.code =
|
|
4144
|
+
this.code = CommonErrorCodes.EXTERNAL_SERVICE_ERROR;
|
|
4098
4145
|
this.domain = ErrorDomains.EXTERNAL;
|
|
4099
4146
|
this.category = exports.ErrorCategory.INFRASTRUCTURE;
|
|
4100
4147
|
this.retryable = true;
|
|
@@ -5142,6 +5189,66 @@ class TokenApi {
|
|
|
5142
5189
|
}
|
|
5143
5190
|
return this.apiClient.get(url);
|
|
5144
5191
|
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Get all token metadata with filtering, pagination, and include relations
|
|
5194
|
+
*
|
|
5195
|
+
* Useful for displaying rewards (ERC1155) or stamps (ERC721) in tables.
|
|
5196
|
+
* Non-admin users always get active metadata only.
|
|
5197
|
+
*
|
|
5198
|
+
* @param options - Filter, pagination, and include options
|
|
5199
|
+
* @returns Paginated response with token metadata
|
|
5200
|
+
*
|
|
5201
|
+
* @example Get all active rewards with mint/burn counts
|
|
5202
|
+
* ```typescript
|
|
5203
|
+
* const rewards = await tokenApi.getTokenMetadata({
|
|
5204
|
+
* tokenType: 'ERC1155',
|
|
5205
|
+
* active: true,
|
|
5206
|
+
* limit: 10,
|
|
5207
|
+
* include: ['mintCount', 'burnCount', 'token']
|
|
5208
|
+
* });
|
|
5209
|
+
* ```
|
|
5210
|
+
*
|
|
5211
|
+
* @example Get stamps with owner business included
|
|
5212
|
+
* ```typescript
|
|
5213
|
+
* const stamps = await tokenApi.getTokenMetadata({
|
|
5214
|
+
* tokenType: 'ERC721',
|
|
5215
|
+
* search: 'gold',
|
|
5216
|
+
* include: ['ownerBusiness', 'token']
|
|
5217
|
+
* });
|
|
5218
|
+
* ```
|
|
5219
|
+
*/
|
|
5220
|
+
async getTokenMetadata(options) {
|
|
5221
|
+
const params = buildPaginationParams(options);
|
|
5222
|
+
if (options?.active !== undefined) {
|
|
5223
|
+
params.set('active', String(options.active));
|
|
5224
|
+
}
|
|
5225
|
+
if (options?.tokenType) {
|
|
5226
|
+
params.set('tokenType', options.tokenType);
|
|
5227
|
+
}
|
|
5228
|
+
if (options?.tokenId) {
|
|
5229
|
+
params.set('tokenId', options.tokenId);
|
|
5230
|
+
}
|
|
5231
|
+
if (options?.search) {
|
|
5232
|
+
params.set('search', options.search);
|
|
5233
|
+
}
|
|
5234
|
+
// Handle tags as string or array
|
|
5235
|
+
if (options?.tags) {
|
|
5236
|
+
const tagsArray = Array.isArray(options.tags) ? options.tags : [options.tags];
|
|
5237
|
+
if (tagsArray.length > 0) {
|
|
5238
|
+
params.set('tags', tagsArray.join(','));
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
// Filter by owner business
|
|
5242
|
+
if (options?.ownerBusinessId) {
|
|
5243
|
+
params.set('ownerBusinessId', options.ownerBusinessId);
|
|
5244
|
+
}
|
|
5245
|
+
// Include relations (ownerBusiness, mintCount, burnCount, token)
|
|
5246
|
+
if (options?.include && options.include.length > 0) {
|
|
5247
|
+
params.set('include', options.include.join(','));
|
|
5248
|
+
}
|
|
5249
|
+
const response = await this.apiClient.get(`${this.basePath}/metadata?${params.toString()}`);
|
|
5250
|
+
return normalizeToPaginated(response);
|
|
5251
|
+
}
|
|
5145
5252
|
// ==========================================
|
|
5146
5253
|
// ADMIN OPERATIONS
|
|
5147
5254
|
// ==========================================
|
|
@@ -5198,6 +5305,30 @@ class TokenApi {
|
|
|
5198
5305
|
async createTokenType(tokenType) {
|
|
5199
5306
|
return this.apiClient.post(`${this.basePath}/types`, tokenType);
|
|
5200
5307
|
}
|
|
5308
|
+
/**
|
|
5309
|
+
* ADMIN: Delete token metadata (soft delete)
|
|
5310
|
+
*
|
|
5311
|
+
* Soft deletes token metadata by setting deletedAt timestamp.
|
|
5312
|
+
* Will fail with 409 Conflict if metadata is in use by active campaigns or redemptions.
|
|
5313
|
+
*
|
|
5314
|
+
* @param metadataId - ID of the token metadata to delete
|
|
5315
|
+
* @throws {PersApiError} 409 Conflict if metadata is in use
|
|
5316
|
+
*
|
|
5317
|
+
* @example
|
|
5318
|
+
* ```typescript
|
|
5319
|
+
* try {
|
|
5320
|
+
* await tokenApi.deleteTokenMetadata('metadata-123');
|
|
5321
|
+
* console.log('Metadata deleted');
|
|
5322
|
+
* } catch (error) {
|
|
5323
|
+
* if (error.statusCode === 409) {
|
|
5324
|
+
* console.log('Cannot delete: metadata is in use');
|
|
5325
|
+
* }
|
|
5326
|
+
* }
|
|
5327
|
+
* ```
|
|
5328
|
+
*/
|
|
5329
|
+
async deleteTokenMetadata(metadataId) {
|
|
5330
|
+
return this.apiClient.delete(`${this.basePath}/metadata/${metadataId}`);
|
|
5331
|
+
}
|
|
5201
5332
|
}
|
|
5202
5333
|
|
|
5203
5334
|
/**
|
|
@@ -5251,6 +5382,62 @@ class TokenService {
|
|
|
5251
5382
|
async getTokenByContractAddress(contractAddress, contractTokenId) {
|
|
5252
5383
|
return this.tokenApi.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
5253
5384
|
}
|
|
5385
|
+
/**
|
|
5386
|
+
* Get all token metadata with filtering, pagination, and include relations
|
|
5387
|
+
*
|
|
5388
|
+
* Useful for displaying rewards (ERC1155) or stamps (ERC721) in tables.
|
|
5389
|
+
* Non-admin users always get active metadata only.
|
|
5390
|
+
*
|
|
5391
|
+
* @param options - Filter, pagination, and include options
|
|
5392
|
+
* @returns Paginated response with token metadata
|
|
5393
|
+
*
|
|
5394
|
+
* @example Get rewards with counts
|
|
5395
|
+
* ```typescript
|
|
5396
|
+
* const rewards = await tokenService.getTokenMetadata({
|
|
5397
|
+
* tokenType: 'ERC1155',
|
|
5398
|
+
* include: ['mintCount', 'burnCount', 'token']
|
|
5399
|
+
* });
|
|
5400
|
+
* ```
|
|
5401
|
+
*/
|
|
5402
|
+
async getTokenMetadata(options) {
|
|
5403
|
+
return this.tokenApi.getTokenMetadata(options);
|
|
5404
|
+
}
|
|
5405
|
+
/**
|
|
5406
|
+
* Get rewards (ERC1155 token metadata) with filtering, pagination, and include relations
|
|
5407
|
+
*
|
|
5408
|
+
* Convenience method for fetching reward metadata.
|
|
5409
|
+
*
|
|
5410
|
+
* @param options - Filter, pagination, and include options (tokenType is set automatically)
|
|
5411
|
+
* @returns Paginated response with reward metadata
|
|
5412
|
+
*
|
|
5413
|
+
* @example Get rewards with mint/burn counts and token info
|
|
5414
|
+
* ```typescript
|
|
5415
|
+
* const rewards = await tokenService.getRewards({
|
|
5416
|
+
* include: ['mintCount', 'burnCount', 'token', 'ownerBusiness']
|
|
5417
|
+
* });
|
|
5418
|
+
* ```
|
|
5419
|
+
*/
|
|
5420
|
+
async getRewards(options) {
|
|
5421
|
+
return this.tokenApi.getTokenMetadata({ ...options, tokenType: NativeTokenTypes.ERC1155 });
|
|
5422
|
+
}
|
|
5423
|
+
/**
|
|
5424
|
+
* Get stamps (ERC721 token metadata) with filtering, pagination, and include relations
|
|
5425
|
+
*
|
|
5426
|
+
* Convenience method for fetching stamp metadata.
|
|
5427
|
+
*
|
|
5428
|
+
* @param options - Filter, pagination, and include options (tokenType is set automatically)
|
|
5429
|
+
* @returns Paginated response with stamp metadata
|
|
5430
|
+
*
|
|
5431
|
+
* @example Get stamps with mint/burn counts and token info
|
|
5432
|
+
* ```typescript
|
|
5433
|
+
* const stamps = await tokenService.getStamps({
|
|
5434
|
+
* include: ['mintCount', 'burnCount', 'token', 'ownerBusiness']
|
|
5435
|
+
* });
|
|
5436
|
+
* ```
|
|
5437
|
+
*/
|
|
5438
|
+
async getStamps(options) {
|
|
5439
|
+
return this.tokenApi.getTokenMetadata({ ...options, tokenType: NativeTokenTypes.ERC721 });
|
|
5440
|
+
}
|
|
5254
5441
|
// ==========================================
|
|
5255
5442
|
// ADMIN OPERATIONS
|
|
5256
5443
|
// ==========================================
|
|
@@ -5303,6 +5490,17 @@ class TokenService {
|
|
|
5303
5490
|
async createTokenType(tokenType) {
|
|
5304
5491
|
return this.tokenApi.createTokenType(tokenType);
|
|
5305
5492
|
}
|
|
5493
|
+
/**
|
|
5494
|
+
* ADMIN: Delete token metadata (soft delete)
|
|
5495
|
+
*
|
|
5496
|
+
* Soft deletes token metadata. Will fail if metadata is in use.
|
|
5497
|
+
*
|
|
5498
|
+
* @param metadataId - ID of the token metadata to delete
|
|
5499
|
+
* @throws {PersApiError} 409 Conflict if metadata is in use by campaigns/redemptions
|
|
5500
|
+
*/
|
|
5501
|
+
async deleteTokenMetadata(metadataId) {
|
|
5502
|
+
return this.tokenApi.deleteTokenMetadata(metadataId);
|
|
5503
|
+
}
|
|
5306
5504
|
}
|
|
5307
5505
|
|
|
5308
5506
|
/**
|
|
@@ -5377,7 +5575,7 @@ class BusinessApi {
|
|
|
5377
5575
|
/**
|
|
5378
5576
|
* Get all businesses with role-based filtering and pagination
|
|
5379
5577
|
*
|
|
5380
|
-
* Endpoint: GET /businesses?active={boolean}&sanitize={mode}&page={number}&limit={number}
|
|
5578
|
+
* Endpoint: GET /businesses?active={boolean}&sanitize={mode}&page={number}&limit={number}&businessTypeId={number}
|
|
5381
5579
|
* Auth: @ApiSecurity('projectKey') (enhanced with role-based filtering)
|
|
5382
5580
|
*
|
|
5383
5581
|
* Note:
|
|
@@ -5400,8 +5598,15 @@ class BusinessApi {
|
|
|
5400
5598
|
* });
|
|
5401
5599
|
*
|
|
5402
5600
|
* // Filter by tag(s)
|
|
5403
|
-
* const tagged = await businessApi.getBusinesses({
|
|
5404
|
-
* const multiTag = await businessApi.getBusinesses({
|
|
5601
|
+
* const tagged = await businessApi.getBusinesses({ tags: 'restaurant' });
|
|
5602
|
+
* const multiTag = await businessApi.getBusinesses({ tags: ['restaurant', 'cafe'] });
|
|
5603
|
+
*
|
|
5604
|
+
* // Filter by business type
|
|
5605
|
+
* const hotels = await businessApi.getBusinesses({ businessTypeId: 1 });
|
|
5606
|
+
*
|
|
5607
|
+
* // Filter by location (geocoding)
|
|
5608
|
+
* const dohaBusinesses = await businessApi.getBusinesses({ city: 'Doha', countryCode: 'QA' });
|
|
5609
|
+
* const westBay = await businessApi.getBusinesses({ neighborhood: 'West Bay' });
|
|
5405
5610
|
* ```
|
|
5406
5611
|
*/
|
|
5407
5612
|
async getBusinesses(options) {
|
|
@@ -5421,6 +5626,25 @@ class BusinessApi {
|
|
|
5421
5626
|
if (options?.sanitize) {
|
|
5422
5627
|
params.set('sanitize', options.sanitize);
|
|
5423
5628
|
}
|
|
5629
|
+
if (options?.businessTypeId !== undefined) {
|
|
5630
|
+
params.set('businessTypeId', String(options.businessTypeId));
|
|
5631
|
+
}
|
|
5632
|
+
// Location filter options (geocoding)
|
|
5633
|
+
if (options?.city) {
|
|
5634
|
+
params.set('city', options.city);
|
|
5635
|
+
}
|
|
5636
|
+
if (options?.countryCode) {
|
|
5637
|
+
params.set('countryCode', options.countryCode);
|
|
5638
|
+
}
|
|
5639
|
+
if (options?.country) {
|
|
5640
|
+
params.set('country', options.country);
|
|
5641
|
+
}
|
|
5642
|
+
if (options?.neighborhood) {
|
|
5643
|
+
params.set('neighborhood', options.neighborhood);
|
|
5644
|
+
}
|
|
5645
|
+
if (options?.district) {
|
|
5646
|
+
params.set('district', options.district);
|
|
5647
|
+
}
|
|
5424
5648
|
const response = await this.apiClient.get(`${this.basePath}?${params.toString()}`);
|
|
5425
5649
|
return normalizeToPaginated(response);
|
|
5426
5650
|
}
|
|
@@ -5767,20 +5991,16 @@ class BusinessService {
|
|
|
5767
5991
|
return this.businessApi.getBusinessByAccount(accountAddress);
|
|
5768
5992
|
}
|
|
5769
5993
|
/**
|
|
5770
|
-
* Get businesses by type (
|
|
5771
|
-
*
|
|
5994
|
+
* Get businesses by type (server-side filtering)
|
|
5995
|
+
* Uses the businessTypeId parameter for efficient server-side filtering.
|
|
5772
5996
|
*/
|
|
5773
5997
|
async getBusinessesByType(typeId, options) {
|
|
5774
|
-
const
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
total: filtered.length,
|
|
5781
|
-
pages: Math.ceil(filtered.length / (response.pagination.limit || 20))
|
|
5782
|
-
}
|
|
5783
|
-
};
|
|
5998
|
+
const businessTypeId = parseInt(typeId, 10);
|
|
5999
|
+
return this.businessApi.getBusinesses({
|
|
6000
|
+
...options,
|
|
6001
|
+
businessTypeId,
|
|
6002
|
+
active: options?.active ?? true // Default to active businesses
|
|
6003
|
+
});
|
|
5784
6004
|
}
|
|
5785
6005
|
// ==========================================
|
|
5786
6006
|
// ADMIN OPERATIONS
|
|
@@ -7019,6 +7239,14 @@ class RedemptionService {
|
|
|
7019
7239
|
async getRedemptions(options) {
|
|
7020
7240
|
return this.redemptionApi.getRedemptions(options);
|
|
7021
7241
|
}
|
|
7242
|
+
/**
|
|
7243
|
+
* Get a specific redemption by ID
|
|
7244
|
+
* @param id - Redemption UUID
|
|
7245
|
+
* @param include - Optional relations to include: 'redeemCount'
|
|
7246
|
+
*/
|
|
7247
|
+
async getRedemptionById(id, include) {
|
|
7248
|
+
return this.redemptionApi.getRedemptionById(id, include);
|
|
7249
|
+
}
|
|
7022
7250
|
/**
|
|
7023
7251
|
* Convenience: Get active redemptions (public)
|
|
7024
7252
|
*/
|
|
@@ -7118,6 +7346,12 @@ class RedemptionService {
|
|
|
7118
7346
|
async updateRedemptionType(data) {
|
|
7119
7347
|
return this.redemptionApi.updateRedemptionType(data);
|
|
7120
7348
|
}
|
|
7349
|
+
/**
|
|
7350
|
+
* ADMIN: Delete redemption
|
|
7351
|
+
*/
|
|
7352
|
+
async deleteRedemption(id) {
|
|
7353
|
+
return this.redemptionApi.deleteRedemption(id);
|
|
7354
|
+
}
|
|
7121
7355
|
/**
|
|
7122
7356
|
* ADMIN: Delete redemption type
|
|
7123
7357
|
*/
|
|
@@ -9053,7 +9287,7 @@ class TriggerSourceApi {
|
|
|
9053
9287
|
/**
|
|
9054
9288
|
* PUBLIC: Get trigger sources with optional filters and pagination
|
|
9055
9289
|
*
|
|
9056
|
-
* @param options - Filter and pagination options
|
|
9290
|
+
* @param options - Filter and pagination options (includes location filters)
|
|
9057
9291
|
* @returns Paginated list of trigger sources
|
|
9058
9292
|
*
|
|
9059
9293
|
* @example
|
|
@@ -9067,6 +9301,12 @@ class TriggerSourceApi {
|
|
|
9067
9301
|
* page: 1,
|
|
9068
9302
|
* limit: 20
|
|
9069
9303
|
* });
|
|
9304
|
+
*
|
|
9305
|
+
* // Filter by location (geocoding)
|
|
9306
|
+
* const dubaiSources = await triggerSourceApi.getTriggerSources({
|
|
9307
|
+
* city: 'Dubai',
|
|
9308
|
+
* countryCode: 'AE'
|
|
9309
|
+
* });
|
|
9070
9310
|
* ```
|
|
9071
9311
|
*/
|
|
9072
9312
|
async getTriggerSources(options) {
|
|
@@ -9079,6 +9319,19 @@ class TriggerSourceApi {
|
|
|
9079
9319
|
params.set('campaignId', options.campaignId);
|
|
9080
9320
|
if (options?.active !== undefined)
|
|
9081
9321
|
params.set('active', String(options.active));
|
|
9322
|
+
if (options?.includeDeleted)
|
|
9323
|
+
params.set('includeDeleted', String(options.includeDeleted));
|
|
9324
|
+
// Location filter options (geocoding)
|
|
9325
|
+
if (options?.city)
|
|
9326
|
+
params.set('city', options.city);
|
|
9327
|
+
if (options?.countryCode)
|
|
9328
|
+
params.set('countryCode', options.countryCode);
|
|
9329
|
+
if (options?.country)
|
|
9330
|
+
params.set('country', options.country);
|
|
9331
|
+
if (options?.neighborhood)
|
|
9332
|
+
params.set('neighborhood', options.neighborhood);
|
|
9333
|
+
if (options?.district)
|
|
9334
|
+
params.set('district', options.district);
|
|
9082
9335
|
const response = await this.apiClient.get(`/trigger-sources?${params.toString()}`);
|
|
9083
9336
|
return normalizeToPaginated(response);
|
|
9084
9337
|
}
|
|
@@ -9575,13 +9828,17 @@ class AuthService {
|
|
|
9575
9828
|
* Returns true for:
|
|
9576
9829
|
* - Known fatal error codes (REFRESH_TOKEN_EXPIRED, TOKEN_REVOKED, etc.)
|
|
9577
9830
|
* - Fatal error codes in error messages (wrapped errors)
|
|
9578
|
-
* - 401/403 HTTP status codes
|
|
9579
9831
|
*
|
|
9580
|
-
* Returns false for (retryable
|
|
9832
|
+
* Returns false for (retryable/recoverable):
|
|
9581
9833
|
* - Network errors
|
|
9582
9834
|
* - Timeouts
|
|
9583
9835
|
* - 5xx server errors
|
|
9584
9836
|
* - TOKEN_EXPIRED (normal refresh trigger)
|
|
9837
|
+
* - Raw 401/403 without fatal code (should try refresh first)
|
|
9838
|
+
*
|
|
9839
|
+
* NOTE: We intentionally DO NOT treat raw 401/403 status as fatal.
|
|
9840
|
+
* The HTTP client handles 401 by attempting token refresh first.
|
|
9841
|
+
* Only if refresh fails WITH a fatal code do we logout.
|
|
9585
9842
|
*/
|
|
9586
9843
|
isDefinitiveAuthFailure(error) {
|
|
9587
9844
|
if (!error)
|
|
@@ -9596,11 +9853,12 @@ class AuthService {
|
|
|
9596
9853
|
if (isFatalAuthErrorInMessage(errorMessage)) {
|
|
9597
9854
|
return true;
|
|
9598
9855
|
}
|
|
9599
|
-
//
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9856
|
+
// NOTE: We intentionally DO NOT check HTTP status codes here.
|
|
9857
|
+
// A raw 401/403 without a fatal error code should trigger a refresh attempt,
|
|
9858
|
+
// not an immediate logout. The HTTP client (pers-api-client.ts) handles this:
|
|
9859
|
+
// 1. 401 received → try refresh token
|
|
9860
|
+
// 2. Refresh succeeds → retry original request
|
|
9861
|
+
// 3. Refresh fails with fatal CODE → then logout
|
|
9604
9862
|
// Everything else is considered retryable
|
|
9605
9863
|
return false;
|
|
9606
9864
|
}
|
|
@@ -9658,6 +9916,9 @@ class AuthService {
|
|
|
9658
9916
|
* Extracts context (tenantId/businessId) from the current refresh token to ensure
|
|
9659
9917
|
* the recovered session maintains the same context.
|
|
9660
9918
|
*
|
|
9919
|
+
* This is a PUBLIC method that can be called by TokenRefreshManager when internal
|
|
9920
|
+
* refresh fails, to try external (provider token) recovery before giving up.
|
|
9921
|
+
*
|
|
9661
9922
|
* @returns true if session was successfully recovered
|
|
9662
9923
|
*/
|
|
9663
9924
|
async attemptProviderTokenRecovery() {
|
|
@@ -9684,12 +9945,17 @@ class AuthService {
|
|
|
9684
9945
|
const contextClaims = refreshToken ? decodeJwtPayload(refreshToken) : null;
|
|
9685
9946
|
// Re-authenticate using provider token based on auth type
|
|
9686
9947
|
// Pass the context from the previous session to avoid MULTIPLE_CONTEXT_SELECTION_REQUIRED
|
|
9948
|
+
// Note: In AuthJWTPayload, when accountType=BUSINESS, sub contains the businessId
|
|
9687
9949
|
switch (authType) {
|
|
9688
9950
|
case exports.AccountOwnerType.USER:
|
|
9689
9951
|
await this.loginUser(providerToken);
|
|
9690
9952
|
break;
|
|
9691
9953
|
case exports.AccountOwnerType.BUSINESS:
|
|
9692
|
-
|
|
9954
|
+
// For BUSINESS auth, sub contains the businessId when accountType is BUSINESS
|
|
9955
|
+
const businessId = contextClaims?.accountType === exports.AccountOwnerType.BUSINESS
|
|
9956
|
+
? contextClaims.sub
|
|
9957
|
+
: undefined;
|
|
9958
|
+
await this.loginBusiness(providerToken, businessId ? { businessId } : undefined);
|
|
9693
9959
|
break;
|
|
9694
9960
|
case exports.AccountOwnerType.TENANT:
|
|
9695
9961
|
await this.loginTenantAdmin(providerToken, contextClaims?.tenantId ? { tenantId: contextClaims.tenantId } : undefined);
|
|
@@ -9805,6 +10071,8 @@ class TokenRefreshManager {
|
|
|
9805
10071
|
// Retry configuration for transient failures
|
|
9806
10072
|
this.MAX_RETRY_ATTEMPTS = 3;
|
|
9807
10073
|
this.RETRY_DELAY_MS = 1000;
|
|
10074
|
+
// Active refresh promise for deduplication
|
|
10075
|
+
this.activeRefreshPromise = null;
|
|
9808
10076
|
}
|
|
9809
10077
|
/**
|
|
9810
10078
|
* Ensures the access token is valid, refreshing if needed.
|
|
@@ -9905,11 +10173,27 @@ class TokenRefreshManager {
|
|
|
9905
10173
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9906
10174
|
}
|
|
9907
10175
|
/**
|
|
9908
|
-
*
|
|
10176
|
+
* Attempt token refresh: internal first, then provider token fallback.
|
|
10177
|
+
* DEDUPLICATES concurrent calls - all callers share the same promise.
|
|
9909
10178
|
*/
|
|
9910
10179
|
async attemptInternalRefresh() {
|
|
9911
|
-
|
|
9912
|
-
|
|
10180
|
+
if (this.activeRefreshPromise) {
|
|
10181
|
+
return this.activeRefreshPromise;
|
|
10182
|
+
}
|
|
10183
|
+
this.activeRefreshPromise = (async () => {
|
|
10184
|
+
const result = await this.attemptRefreshWithRetry();
|
|
10185
|
+
if (result.success)
|
|
10186
|
+
return result;
|
|
10187
|
+
// Internal failed - try provider token recovery
|
|
10188
|
+
const recovered = await this.authService.attemptProviderTokenRecovery();
|
|
10189
|
+
return recovered ? { success: true, retryable: false } : result;
|
|
10190
|
+
})();
|
|
10191
|
+
try {
|
|
10192
|
+
return await this.activeRefreshPromise;
|
|
10193
|
+
}
|
|
10194
|
+
finally {
|
|
10195
|
+
this.activeRefreshPromise = null;
|
|
10196
|
+
}
|
|
9913
10197
|
}
|
|
9914
10198
|
}
|
|
9915
10199
|
|
|
@@ -10457,17 +10741,16 @@ class DPoPManager {
|
|
|
10457
10741
|
* Generate a short fingerprint of the public key for debugging
|
|
10458
10742
|
* This helps identify if the same key is being used across operations
|
|
10459
10743
|
*/
|
|
10460
|
-
getPublicKeyFingerprint(publicKey) {
|
|
10461
|
-
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
}
|
|
10744
|
+
/* private getPublicKeyFingerprint(publicKey: JsonWebKey): string {
|
|
10745
|
+
try {
|
|
10746
|
+
// Use the 'x' coordinate of the EC key as a fingerprint
|
|
10747
|
+
const x = publicKey.x || '';
|
|
10748
|
+
const y = publicKey.y || '';
|
|
10749
|
+
return `${x.substring(0, 8)}...${y.substring(0, 4)}`;
|
|
10750
|
+
} catch {
|
|
10751
|
+
return 'unknown';
|
|
10752
|
+
}
|
|
10753
|
+
} */
|
|
10471
10754
|
/**
|
|
10472
10755
|
* Clears DPoP keys (e.g. on logout)
|
|
10473
10756
|
*/
|
|
@@ -10716,7 +10999,7 @@ class DefaultAuthProvider {
|
|
|
10716
10999
|
/** SDK package name */
|
|
10717
11000
|
const SDK_NAME = "@explorins/pers-sdk";
|
|
10718
11001
|
/** SDK version - injected from package.json at build time */
|
|
10719
|
-
const SDK_VERSION = "2.1.
|
|
11002
|
+
const SDK_VERSION = "2.1.39";
|
|
10720
11003
|
/** Full SDK identifier for headers */
|
|
10721
11004
|
const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
|
|
10722
11005
|
|
|
@@ -10826,12 +11109,16 @@ class PersApiClient {
|
|
|
10826
11109
|
}
|
|
10827
11110
|
const { retryCount = 0, responseType = 'json', bypassAuth = false } = options || {};
|
|
10828
11111
|
const url = `${this.apiRoot}${endpoint}`;
|
|
10829
|
-
//
|
|
11112
|
+
// TOKEN VALIDATION: Ensure token is valid before request
|
|
11113
|
+
// This blocks the request until token validation completes - prevents 401s from expired tokens
|
|
10830
11114
|
if (!bypassAuth && this.mergedConfig.authProvider && retryCount === 0) {
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
11115
|
+
try {
|
|
11116
|
+
await this.ensureValidToken();
|
|
11117
|
+
}
|
|
11118
|
+
catch (error) {
|
|
11119
|
+
console.debug('[PersApiClient] Token validation failed:', error);
|
|
11120
|
+
// Continue with request - it will fail with 401 and trigger retry logic
|
|
11121
|
+
}
|
|
10835
11122
|
}
|
|
10836
11123
|
const requestOptions = {
|
|
10837
11124
|
headers: await this.getHeaders(!bypassAuth, method, url),
|
|
@@ -10859,49 +11146,63 @@ class PersApiClient {
|
|
|
10859
11146
|
return result;
|
|
10860
11147
|
}
|
|
10861
11148
|
catch (error) {
|
|
10862
|
-
// Error handling - proactive token refresh should prevent most 401s
|
|
10863
11149
|
const status = ErrorUtils.getStatus(error);
|
|
10864
11150
|
const errorMessage = ErrorUtils.getMessage(error);
|
|
10865
|
-
// Handle 401 errors
|
|
11151
|
+
// Handle 401 Unauthorized errors
|
|
10866
11152
|
if (status === 401) {
|
|
10867
11153
|
const backendError = ErrorUtils.extractBackendErrorDetails(error);
|
|
10868
|
-
//
|
|
10869
|
-
//
|
|
10870
|
-
//
|
|
11154
|
+
// ===========================================
|
|
11155
|
+
// FATAL AUTH ERRORS → Immediate logout
|
|
11156
|
+
// ===========================================
|
|
11157
|
+
// Fatal codes: REFRESH_TOKEN_EXPIRED, TOKEN_REVOKED, INVALID_REFRESH_TOKEN, etc.
|
|
10871
11158
|
if (this.authService.isFatalAuthError(backendError.code)) {
|
|
10872
|
-
|
|
11159
|
+
console.warn(`[PersApiClient] Fatal auth error: ${backendError.code}`);
|
|
10873
11160
|
await this.authService.handleAuthFailure();
|
|
10874
11161
|
this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
|
|
10875
11162
|
throw new AuthenticationError(errorMessage, endpoint, method, backendError);
|
|
10876
11163
|
}
|
|
10877
|
-
//
|
|
11164
|
+
// ===========================================
|
|
11165
|
+
// REFRESH REQUEST FAILED → Logout (prevents infinite loop)
|
|
11166
|
+
// ===========================================
|
|
10878
11167
|
if (options?.isRefreshRequest) {
|
|
10879
|
-
|
|
11168
|
+
console.warn('[PersApiClient] Refresh request itself failed with 401');
|
|
10880
11169
|
await this.authService.handleAuthFailure();
|
|
10881
11170
|
this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
|
|
10882
11171
|
throw new AuthenticationError(errorMessage, endpoint, method, backendError);
|
|
10883
11172
|
}
|
|
10884
|
-
//
|
|
11173
|
+
// ===========================================
|
|
11174
|
+
// RETRYABLE 401 (TOKEN_EXPIRED) → Try refresh, then retry
|
|
11175
|
+
// ===========================================
|
|
10885
11176
|
if (retryCount === 0 && this.mergedConfig.authProvider) {
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
// Retry with new token
|
|
10896
|
-
return await this.request(method, endpoint, body, { ...options, retryCount: 1 });
|
|
10897
|
-
}
|
|
11177
|
+
const refreshResult = await this.refreshManager.attemptInternalRefresh();
|
|
11178
|
+
if (refreshResult.success) {
|
|
11179
|
+
// Refresh succeeded → retry request with new token
|
|
11180
|
+
this._events?.emitSuccess({
|
|
11181
|
+
type: 'token_refreshed',
|
|
11182
|
+
domain: 'authentication',
|
|
11183
|
+
userMessage: 'Authentication token refreshed',
|
|
11184
|
+
});
|
|
11185
|
+
return await this.request(method, endpoint, body, { ...options, retryCount: 1 });
|
|
10898
11186
|
}
|
|
10899
|
-
|
|
10900
|
-
|
|
11187
|
+
// Refresh failed - check if it's a definitive failure
|
|
11188
|
+
if (!refreshResult.retryable) {
|
|
11189
|
+
// Non-retryable failure (e.g., refresh token expired) → logout
|
|
11190
|
+
console.warn('[PersApiClient] Refresh failed with non-retryable error, logging out');
|
|
11191
|
+
await this.authService.handleAuthFailure();
|
|
10901
11192
|
}
|
|
11193
|
+
else {
|
|
11194
|
+
// Retryable failure (network error, etc.) → DON'T logout, let app retry later
|
|
11195
|
+
console.warn('[PersApiClient] Refresh failed with retryable error, NOT logging out');
|
|
11196
|
+
}
|
|
11197
|
+
this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
|
|
11198
|
+
throw new AuthenticationError(errorMessage, endpoint, method, backendError);
|
|
10902
11199
|
}
|
|
10903
|
-
//
|
|
10904
|
-
|
|
11200
|
+
// ===========================================
|
|
11201
|
+
// RETRY FAILED (retryCount > 0) → Something wrong, but DON'T logout
|
|
11202
|
+
// ===========================================
|
|
11203
|
+
// We already refreshed successfully, but retry still got 401
|
|
11204
|
+
// This is unexpected - don't logout, let app investigate
|
|
11205
|
+
console.warn('[PersApiClient] Retry with new token still got 401 - not logging out');
|
|
10905
11206
|
this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
|
|
10906
11207
|
throw new AuthenticationError(errorMessage, endpoint, method, backendError);
|
|
10907
11208
|
}
|
|
@@ -10927,9 +11228,9 @@ class PersApiClient {
|
|
|
10927
11228
|
}); */
|
|
10928
11229
|
this._events?.emitError({
|
|
10929
11230
|
domain: errorDetails.domain || 'external',
|
|
10930
|
-
type:
|
|
10931
|
-
userMessage: errorDetails.message || errorMessage,
|
|
10932
|
-
code: errorDetails.code,
|
|
11231
|
+
type: 'api_error', // Event type discriminator (fixed value)
|
|
11232
|
+
userMessage: errorDetails.message || errorMessage,
|
|
11233
|
+
code: errorDetails.code, // Actual error code from backend
|
|
10933
11234
|
details: {
|
|
10934
11235
|
endpoint,
|
|
10935
11236
|
method,
|
|
@@ -12565,6 +12866,107 @@ class TokenManager {
|
|
|
12565
12866
|
async getTokenByContract(contractAddress, contractTokenId = null) {
|
|
12566
12867
|
return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
|
|
12567
12868
|
}
|
|
12869
|
+
/**
|
|
12870
|
+
* Get all token metadata with filtering, pagination, and include relations
|
|
12871
|
+
*
|
|
12872
|
+
* Retrieves token metadata (rewards/stamps) with server-side filtering, pagination,
|
|
12873
|
+
* and optional include relations for additional data.
|
|
12874
|
+
* Useful for displaying rewards (ERC1155) or stamps (ERC721) in admin tables.
|
|
12875
|
+
* Non-admin users always get active metadata only.
|
|
12876
|
+
*
|
|
12877
|
+
* @param options - Filter, pagination, and include options
|
|
12878
|
+
* @returns Promise resolving to paginated token metadata
|
|
12879
|
+
*
|
|
12880
|
+
* @example Get paginated rewards with counts
|
|
12881
|
+
* ```typescript
|
|
12882
|
+
* const rewards = await sdk.tokens.getTokenMetadata({
|
|
12883
|
+
* tokenType: 'ERC1155',
|
|
12884
|
+
* active: true,
|
|
12885
|
+
* page: 1,
|
|
12886
|
+
* limit: 10,
|
|
12887
|
+
* sortBy: 'name',
|
|
12888
|
+
* sortOrder: 'ASC',
|
|
12889
|
+
* include: ['mintCount', 'burnCount', 'token']
|
|
12890
|
+
* });
|
|
12891
|
+
*
|
|
12892
|
+
* rewards.data.forEach(r => {
|
|
12893
|
+
* console.log(`${r.name}: ${r.mintCount} minted, ${r.burnCount} burned`);
|
|
12894
|
+
* console.log(`Contract: ${r.included?.token?.contractAddress}`);
|
|
12895
|
+
* });
|
|
12896
|
+
* ```
|
|
12897
|
+
*
|
|
12898
|
+
* @example Get stamps with owner business
|
|
12899
|
+
* ```typescript
|
|
12900
|
+
* const stamps = await sdk.tokens.getTokenMetadata({
|
|
12901
|
+
* tokenType: 'ERC721',
|
|
12902
|
+
* search: 'gold',
|
|
12903
|
+
* include: ['ownerBusiness', 'token']
|
|
12904
|
+
* });
|
|
12905
|
+
*
|
|
12906
|
+
* stamps.data.forEach(s => {
|
|
12907
|
+
* console.log(`${s.name} owned by ${s.included?.ownerBusiness?.displayName}`);
|
|
12908
|
+
* });
|
|
12909
|
+
* ```
|
|
12910
|
+
*/
|
|
12911
|
+
async getTokenMetadata(options) {
|
|
12912
|
+
return this.tokenService.getTokenMetadata(options);
|
|
12913
|
+
}
|
|
12914
|
+
/**
|
|
12915
|
+
* Get rewards (ERC1155 token metadata) with filtering, pagination, and include relations
|
|
12916
|
+
*
|
|
12917
|
+
* Convenience method for fetching reward metadata. Rewards are ERC1155 tokens
|
|
12918
|
+
* that represent collectible items, vouchers, or other redeemable rewards.
|
|
12919
|
+
*
|
|
12920
|
+
* @param options - Filter, pagination, and include options (tokenType is set automatically)
|
|
12921
|
+
* @returns Promise resolving to paginated reward metadata
|
|
12922
|
+
*
|
|
12923
|
+
* @example Get active rewards with mint counts
|
|
12924
|
+
* ```typescript
|
|
12925
|
+
* const rewards = await sdk.tokens.getRewards({
|
|
12926
|
+
* active: true,
|
|
12927
|
+
* limit: 10,
|
|
12928
|
+
* include: ['mintCount', 'burnCount', 'token', 'ownerBusiness']
|
|
12929
|
+
* });
|
|
12930
|
+
*
|
|
12931
|
+
* rewards.data.forEach(r => {
|
|
12932
|
+
* console.log(`${r.name}: ${r.mintCount ?? 0} minted`);
|
|
12933
|
+
* if (r.included?.token) {
|
|
12934
|
+
* console.log(` Chain: ${r.included.token.chainId}, Contract: ${r.included.token.contractAddress}`);
|
|
12935
|
+
* }
|
|
12936
|
+
* });
|
|
12937
|
+
* ```
|
|
12938
|
+
*/
|
|
12939
|
+
async getRewards(options) {
|
|
12940
|
+
return this.tokenService.getRewards(options);
|
|
12941
|
+
}
|
|
12942
|
+
/**
|
|
12943
|
+
* Get stamps (ERC721 token metadata) with filtering, pagination, and include relations
|
|
12944
|
+
*
|
|
12945
|
+
* Convenience method for fetching stamp metadata. Stamps are ERC721 tokens
|
|
12946
|
+
* that represent achievements, badges, or collectible status markers.
|
|
12947
|
+
*
|
|
12948
|
+
* @param options - Filter, pagination, and include options (tokenType is set automatically)
|
|
12949
|
+
* @returns Promise resolving to paginated stamp metadata
|
|
12950
|
+
*
|
|
12951
|
+
* @example Get active stamps with all includes
|
|
12952
|
+
* ```typescript
|
|
12953
|
+
* const stamps = await sdk.tokens.getStamps({
|
|
12954
|
+
* active: true,
|
|
12955
|
+
* limit: 10,
|
|
12956
|
+
* include: ['mintCount', 'burnCount', 'token', 'ownerBusiness']
|
|
12957
|
+
* });
|
|
12958
|
+
*
|
|
12959
|
+
* stamps.data.forEach(s => {
|
|
12960
|
+
* console.log(`${s.name}: ${s.mintCount ?? 0} minted`);
|
|
12961
|
+
* if (s.included?.ownerBusiness) {
|
|
12962
|
+
* console.log(` Owner: ${s.included.ownerBusiness.displayName}`);
|
|
12963
|
+
* }
|
|
12964
|
+
* });
|
|
12965
|
+
* ```
|
|
12966
|
+
*/
|
|
12967
|
+
async getStamps(options) {
|
|
12968
|
+
return this.tokenService.getStamps(options);
|
|
12969
|
+
}
|
|
12568
12970
|
/**
|
|
12569
12971
|
* Admin: Create new token
|
|
12570
12972
|
*
|
|
@@ -12656,6 +13058,32 @@ class TokenManager {
|
|
|
12656
13058
|
async toggleTokenActive(tokenId) {
|
|
12657
13059
|
return this.tokenService.toggleTokenActive(tokenId);
|
|
12658
13060
|
}
|
|
13061
|
+
/**
|
|
13062
|
+
* Admin: Delete token metadata (soft delete)
|
|
13063
|
+
*
|
|
13064
|
+
* Soft deletes token metadata by setting deletedAt timestamp.
|
|
13065
|
+
* Requires administrator privileges. Will fail with 409 Conflict
|
|
13066
|
+
* if metadata is currently in use by active campaigns or redemptions.
|
|
13067
|
+
*
|
|
13068
|
+
* @param metadataId - ID of the token metadata to delete
|
|
13069
|
+
* @throws {PersApiError} When not authenticated as admin
|
|
13070
|
+
* @throws {PersApiError} 409 Conflict if metadata is in use
|
|
13071
|
+
*
|
|
13072
|
+
* @example
|
|
13073
|
+
* ```typescript
|
|
13074
|
+
* try {
|
|
13075
|
+
* await sdk.tokens.deleteTokenMetadata('metadata-123');
|
|
13076
|
+
* console.log('Token metadata deleted');
|
|
13077
|
+
* } catch (error) {
|
|
13078
|
+
* if (error.statusCode === 409) {
|
|
13079
|
+
* console.log('Cannot delete: metadata is used by campaigns/redemptions');
|
|
13080
|
+
* }
|
|
13081
|
+
* }
|
|
13082
|
+
* ```
|
|
13083
|
+
*/
|
|
13084
|
+
async deleteTokenMetadata(metadataId) {
|
|
13085
|
+
return this.tokenService.deleteTokenMetadata(metadataId);
|
|
13086
|
+
}
|
|
12659
13087
|
/**
|
|
12660
13088
|
* Get the full token SDK for advanced operations
|
|
12661
13089
|
*
|
|
@@ -14469,6 +14897,52 @@ class RedemptionManager {
|
|
|
14469
14897
|
async getRedemptions(options) {
|
|
14470
14898
|
return this.redemptionService.getRedemptions(options);
|
|
14471
14899
|
}
|
|
14900
|
+
/**
|
|
14901
|
+
* Get a specific redemption by ID
|
|
14902
|
+
*
|
|
14903
|
+
* Retrieves detailed information about a specific redemption offer.
|
|
14904
|
+
* Useful when you already have the redemption ID and need full details.
|
|
14905
|
+
*
|
|
14906
|
+
* @param id - Redemption UUID
|
|
14907
|
+
* @param include - Optional relations to include: 'redeemCount'
|
|
14908
|
+
* @returns Promise resolving to the redemption
|
|
14909
|
+
*
|
|
14910
|
+
* @example Basic usage
|
|
14911
|
+
* ```typescript
|
|
14912
|
+
* const redemption = await sdk.redemptions.getRedemptionById('7baf4d39-0bd6-45ca-9c66-8c0d655767c3');
|
|
14913
|
+
* console.log(redemption.name);
|
|
14914
|
+
* console.log(redemption.description);
|
|
14915
|
+
* ```
|
|
14916
|
+
*
|
|
14917
|
+
* @example With redeem count
|
|
14918
|
+
* ```typescript
|
|
14919
|
+
* const redemption = await sdk.redemptions.getRedemptionById(
|
|
14920
|
+
* 'redemption-123',
|
|
14921
|
+
* ['redeemCount']
|
|
14922
|
+
* );
|
|
14923
|
+
* console.log(`${redemption.name} has been redeemed ${redemption.redeemCount} times`);
|
|
14924
|
+
* ```
|
|
14925
|
+
*
|
|
14926
|
+
* @example Get redemption details for display
|
|
14927
|
+
* ```typescript
|
|
14928
|
+
* const redemption = await sdk.redemptions.getRedemptionById(id);
|
|
14929
|
+
*
|
|
14930
|
+
* console.log('Redemption Details:');
|
|
14931
|
+
* console.log(`Name: ${redemption.name}`);
|
|
14932
|
+
* console.log(`Description: ${redemption.description}`);
|
|
14933
|
+
* console.log(`Active: ${redemption.isActive}`);
|
|
14934
|
+
*
|
|
14935
|
+
* if (redemption.tokenUnits?.length) {
|
|
14936
|
+
* console.log('Cost:');
|
|
14937
|
+
* redemption.tokenUnits.forEach(unit => {
|
|
14938
|
+
* console.log(` ${unit.amount} ${unit.token.symbol}`);
|
|
14939
|
+
* });
|
|
14940
|
+
* }
|
|
14941
|
+
* ```
|
|
14942
|
+
*/
|
|
14943
|
+
async getRedemptionById(id, include) {
|
|
14944
|
+
return this.redemptionService.getRedemptionById(id, include);
|
|
14945
|
+
}
|
|
14472
14946
|
/**
|
|
14473
14947
|
* Get available redemption types
|
|
14474
14948
|
*
|
|
@@ -15003,6 +15477,21 @@ class RedemptionManager {
|
|
|
15003
15477
|
async deleteRedemptionType(id) {
|
|
15004
15478
|
return this.redemptionService.deleteRedemptionType(id);
|
|
15005
15479
|
}
|
|
15480
|
+
/**
|
|
15481
|
+
* Delete a redemption (soft delete)
|
|
15482
|
+
*
|
|
15483
|
+
* @param id - Redemption ID to delete
|
|
15484
|
+
* @returns Promise resolving to true if deleted successfully
|
|
15485
|
+
*
|
|
15486
|
+
* @example
|
|
15487
|
+
* ```typescript
|
|
15488
|
+
* const success = await sdk.redemptions.deleteRedemption('redemption-123');
|
|
15489
|
+
* console.log('Deleted:', success);
|
|
15490
|
+
* ```
|
|
15491
|
+
*/
|
|
15492
|
+
async deleteRedemption(id) {
|
|
15493
|
+
return this.redemptionService.deleteRedemption(id);
|
|
15494
|
+
}
|
|
15006
15495
|
/**
|
|
15007
15496
|
* Get the full redemption service for advanced operations
|
|
15008
15497
|
*
|
|
@@ -21157,6 +21646,99 @@ const useTokens = () => {
|
|
|
21157
21646
|
throw error;
|
|
21158
21647
|
}
|
|
21159
21648
|
}, [sdk, isInitialized]);
|
|
21649
|
+
/**
|
|
21650
|
+
* Get all token metadata with filtering and pagination
|
|
21651
|
+
*
|
|
21652
|
+
* Retrieves token metadata (rewards/stamps) with server-side filtering.
|
|
21653
|
+
* Use tokenType: 'ERC1155' for rewards, 'ERC721' for stamps.
|
|
21654
|
+
*
|
|
21655
|
+
* @param options - Filter and pagination options
|
|
21656
|
+
* @returns Promise resolving to paginated token metadata
|
|
21657
|
+
* @throws Error if SDK is not initialized
|
|
21658
|
+
*
|
|
21659
|
+
* @example
|
|
21660
|
+
* ```typescript
|
|
21661
|
+
* const { getTokenMetadata } = useTokens();
|
|
21662
|
+
* const rewards = await getTokenMetadata({
|
|
21663
|
+
* tokenType: 'ERC1155',
|
|
21664
|
+
* active: true,
|
|
21665
|
+
* limit: 10
|
|
21666
|
+
* });
|
|
21667
|
+
* ```
|
|
21668
|
+
*/
|
|
21669
|
+
const getTokenMetadata = react.useCallback(async (options) => {
|
|
21670
|
+
if (!isInitialized || !sdk) {
|
|
21671
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
21672
|
+
}
|
|
21673
|
+
try {
|
|
21674
|
+
const result = await sdk.tokens.getTokenMetadata(options);
|
|
21675
|
+
return result;
|
|
21676
|
+
}
|
|
21677
|
+
catch (error) {
|
|
21678
|
+
console.error('Failed to fetch token metadata:', error);
|
|
21679
|
+
throw error;
|
|
21680
|
+
}
|
|
21681
|
+
}, [sdk, isInitialized]);
|
|
21682
|
+
/**
|
|
21683
|
+
* Get rewards (ERC1155 token metadata) with filtering and pagination
|
|
21684
|
+
*
|
|
21685
|
+
* Convenience method for fetching reward metadata. Rewards are ERC1155 tokens
|
|
21686
|
+
* that represent collectible items, vouchers, or other redeemable rewards.
|
|
21687
|
+
*
|
|
21688
|
+
* @param options - Filter and pagination options
|
|
21689
|
+
* @returns Promise resolving to paginated reward metadata
|
|
21690
|
+
* @throws Error if SDK is not initialized
|
|
21691
|
+
*
|
|
21692
|
+
* @example
|
|
21693
|
+
* ```typescript
|
|
21694
|
+
* const { getRewards } = useTokens();
|
|
21695
|
+
* const rewards = await getRewards({ active: true, limit: 10 });
|
|
21696
|
+
* console.log(`Found ${rewards.pagination.total} rewards`);
|
|
21697
|
+
* ```
|
|
21698
|
+
*/
|
|
21699
|
+
const getRewards = react.useCallback(async (options) => {
|
|
21700
|
+
if (!isInitialized || !sdk) {
|
|
21701
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
21702
|
+
}
|
|
21703
|
+
try {
|
|
21704
|
+
const result = await sdk.tokens.getRewards(options);
|
|
21705
|
+
return result;
|
|
21706
|
+
}
|
|
21707
|
+
catch (error) {
|
|
21708
|
+
console.error('Failed to fetch rewards:', error);
|
|
21709
|
+
throw error;
|
|
21710
|
+
}
|
|
21711
|
+
}, [sdk, isInitialized]);
|
|
21712
|
+
/**
|
|
21713
|
+
* Get stamps (ERC721 token metadata) with filtering and pagination
|
|
21714
|
+
*
|
|
21715
|
+
* Convenience method for fetching stamp metadata. Stamps are ERC721 tokens
|
|
21716
|
+
* that represent achievements, badges, or collectible status markers.
|
|
21717
|
+
*
|
|
21718
|
+
* @param options - Filter and pagination options
|
|
21719
|
+
* @returns Promise resolving to paginated stamp metadata
|
|
21720
|
+
* @throws Error if SDK is not initialized
|
|
21721
|
+
*
|
|
21722
|
+
* @example
|
|
21723
|
+
* ```typescript
|
|
21724
|
+
* const { getStamps } = useTokens();
|
|
21725
|
+
* const stamps = await getStamps({ active: true, limit: 10 });
|
|
21726
|
+
* console.log(`Found ${stamps.pagination.total} stamps`);
|
|
21727
|
+
* ```
|
|
21728
|
+
*/
|
|
21729
|
+
const getStamps = react.useCallback(async (options) => {
|
|
21730
|
+
if (!isInitialized || !sdk) {
|
|
21731
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
21732
|
+
}
|
|
21733
|
+
try {
|
|
21734
|
+
const result = await sdk.tokens.getStamps(options);
|
|
21735
|
+
return result;
|
|
21736
|
+
}
|
|
21737
|
+
catch (error) {
|
|
21738
|
+
console.error('Failed to fetch stamps:', error);
|
|
21739
|
+
throw error;
|
|
21740
|
+
}
|
|
21741
|
+
}, [sdk, isInitialized]);
|
|
21160
21742
|
return {
|
|
21161
21743
|
getTokens,
|
|
21162
21744
|
getActiveCreditToken,
|
|
@@ -21164,6 +21746,9 @@ const useTokens = () => {
|
|
|
21164
21746
|
getTokenTypes,
|
|
21165
21747
|
getStatusTokens,
|
|
21166
21748
|
getTokenByContract,
|
|
21749
|
+
getTokenMetadata,
|
|
21750
|
+
getRewards,
|
|
21751
|
+
getStamps,
|
|
21167
21752
|
isAvailable: isInitialized && !!sdk?.tokens,
|
|
21168
21753
|
};
|
|
21169
21754
|
};
|
|
@@ -44400,6 +44985,25 @@ const useRedemptions = () => {
|
|
|
44400
44985
|
/**
|
|
44401
44986
|
* Redeem a redemption offer with optional dynamic context
|
|
44402
44987
|
*
|
|
44988
|
+
* ## When to use `redeem()` vs `createTransaction()`
|
|
44989
|
+
*
|
|
44990
|
+
* | Use Case | Method | Auto-Signs |
|
|
44991
|
+
* |----------|--------|------------|
|
|
44992
|
+
* | **Voucher redemption** | `redeem()` | ✅ Yes |
|
|
44993
|
+
* | **Reward redemption** | `redeem()` | ✅ Yes |
|
|
44994
|
+
* | **Direct token transfer** | `createTransaction()` | ⚠️ Depends |
|
|
44995
|
+
* | **POS burn with QR** | `buildPOSBurnRequest()` | ❌ Manual |
|
|
44996
|
+
*
|
|
44997
|
+
* **Use `redeem()` for voucher/reward redemptions.** It automatically:
|
|
44998
|
+
* 1. Creates redemption record
|
|
44999
|
+
* 2. Triggers passkey/biometric prompt
|
|
45000
|
+
* 3. Signs the transaction
|
|
45001
|
+
* 4. Submits to blockchain
|
|
45002
|
+
* 5. Returns result with `transactionHash`
|
|
45003
|
+
*
|
|
45004
|
+
* **No admin approval required** - The "PENDING" state is brief (seconds).
|
|
45005
|
+
* The user's passkey signature IS the approval.
|
|
45006
|
+
*
|
|
44403
45007
|
* Supports dynamic context for personalized NFT generation when the redemption
|
|
44404
45008
|
* issues ERC721 tokens. Context can include validity dates, AI prompt placeholders,
|
|
44405
45009
|
* and custom metadata.
|
|
@@ -44408,11 +45012,21 @@ const useRedemptions = () => {
|
|
|
44408
45012
|
* @param options - Optional settings
|
|
44409
45013
|
* @param options.context - Dynamic context for NFT personalization (validity dates, AI prompts)
|
|
44410
45014
|
* @param options.onStatusUpdate - Callback for transaction signing status updates
|
|
44411
|
-
* @returns Promise resolving to redemption result
|
|
45015
|
+
* @returns Promise resolving to redemption result with `transactionHash` on success
|
|
44412
45016
|
*
|
|
44413
|
-
* @example Basic redemption
|
|
45017
|
+
* @example Basic redemption (most common)
|
|
44414
45018
|
* ```typescript
|
|
44415
|
-
* const
|
|
45019
|
+
* const { redeem } = useRedemptions();
|
|
45020
|
+
*
|
|
45021
|
+
* const result = await redeem('redemption-123', {
|
|
45022
|
+
* onStatusUpdate: (status, message) => {
|
|
45023
|
+
* console.log(`Status: ${status} - ${message}`);
|
|
45024
|
+
* }
|
|
45025
|
+
* });
|
|
45026
|
+
*
|
|
45027
|
+
* if (result.transactionHash) {
|
|
45028
|
+
* console.log('Redemption complete!', result.transactionHash);
|
|
45029
|
+
* }
|
|
44416
45030
|
* ```
|
|
44417
45031
|
*
|
|
44418
45032
|
* @example Redemption with dynamic context (personalized NFT)
|
|
@@ -44443,6 +45057,8 @@ const useRedemptions = () => {
|
|
|
44443
45057
|
* }
|
|
44444
45058
|
* });
|
|
44445
45059
|
* ```
|
|
45060
|
+
*
|
|
45061
|
+
* @see {@link useTransactions.createTransaction | createTransaction()} for direct transfers without redemption flow
|
|
44446
45062
|
*/
|
|
44447
45063
|
const redeem = react.useCallback(async (redemptionId, options) => {
|
|
44448
45064
|
if (!isInitialized || !sdk) {
|
|
@@ -44592,8 +45208,43 @@ const useRedemptions = () => {
|
|
|
44592
45208
|
throw error;
|
|
44593
45209
|
}
|
|
44594
45210
|
}, [sdk, isInitialized]);
|
|
45211
|
+
/**
|
|
45212
|
+
* Get a specific redemption by ID
|
|
45213
|
+
*
|
|
45214
|
+
* Retrieves detailed information about a specific redemption offer.
|
|
45215
|
+
*
|
|
45216
|
+
* @param id - Redemption UUID
|
|
45217
|
+
* @param include - Optional relations to include: 'redeemCount'
|
|
45218
|
+
* @returns Promise resolving to the redemption
|
|
45219
|
+
*
|
|
45220
|
+
* @example Basic usage
|
|
45221
|
+
* ```typescript
|
|
45222
|
+
* const redemption = await getRedemptionById('7baf4d39-0bd6-45ca-9c66-8c0d655767c3');
|
|
45223
|
+
* console.log(redemption.name);
|
|
45224
|
+
* ```
|
|
45225
|
+
*
|
|
45226
|
+
* @example With redeem count
|
|
45227
|
+
* ```typescript
|
|
45228
|
+
* const redemption = await getRedemptionById('redemption-123', ['redeemCount']);
|
|
45229
|
+
* console.log(`${redemption.name} has been redeemed ${redemption.redeemCount} times`);
|
|
45230
|
+
* ```
|
|
45231
|
+
*/
|
|
45232
|
+
const getRedemptionById = react.useCallback(async (id, include) => {
|
|
45233
|
+
if (!isInitialized || !sdk) {
|
|
45234
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
45235
|
+
}
|
|
45236
|
+
try {
|
|
45237
|
+
const result = await sdk.redemptions.getRedemptionById(id, include);
|
|
45238
|
+
return result;
|
|
45239
|
+
}
|
|
45240
|
+
catch (error) {
|
|
45241
|
+
console.error('Failed to fetch redemption by ID:', error);
|
|
45242
|
+
throw error;
|
|
45243
|
+
}
|
|
45244
|
+
}, [sdk, isInitialized]);
|
|
44595
45245
|
return {
|
|
44596
45246
|
getRedemptions,
|
|
45247
|
+
getRedemptionById,
|
|
44597
45248
|
getUserRedemptions,
|
|
44598
45249
|
redeem,
|
|
44599
45250
|
getRedemptionTypes,
|
|
@@ -46293,6 +46944,7 @@ exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
|
|
|
46293
46944
|
exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
|
|
46294
46945
|
exports.VALID_REDEMPTION_RELATIONS = VALID_REDEMPTION_RELATIONS;
|
|
46295
46946
|
exports.VALID_RELATIONS = VALID_RELATIONS;
|
|
46947
|
+
exports.VALID_TOKEN_METADATA_RELATIONS = VALID_TOKEN_METADATA_RELATIONS;
|
|
46296
46948
|
exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
|
|
46297
46949
|
exports.VALID_USER_RELATIONS = VALID_USER_RELATIONS;
|
|
46298
46950
|
exports.WalletEventsManager = WalletEventsManager;
|
|
@@ -46344,6 +46996,7 @@ exports.isValidCampaignRelation = isValidCampaignRelation;
|
|
|
46344
46996
|
exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
|
|
46345
46997
|
exports.isValidRedemptionRelation = isValidRedemptionRelation;
|
|
46346
46998
|
exports.isValidRelation = isValidRelation;
|
|
46999
|
+
exports.isValidTokenMetadataRelation = isValidTokenMetadataRelation;
|
|
46347
47000
|
exports.isValidTransactionRelation = isValidTransactionRelation;
|
|
46348
47001
|
exports.isValidUserRelation = isValidUserRelation;
|
|
46349
47002
|
exports.mergeWithDefaults = mergeWithDefaults;
|