@explorins/pers-sdk-react-native 2.2.1 → 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/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)
@@ -3557,7 +3559,8 @@ function isValidTransactionRelation(value) {
3557
3559
  const VALID_CAMPAIGN_RELATIONS = [
3558
3560
  'triggerSources',
3559
3561
  'businesses',
3560
- 'claimCount'
3562
+ 'claimCount',
3563
+ 'ownerBusiness'
3561
3564
  ];
3562
3565
  /**
3563
3566
  * Type guard to validate campaign include relation at runtime
@@ -3595,7 +3598,8 @@ function isValidCampaignClaimRelation(value) {
3595
3598
  * Used for runtime validation
3596
3599
  */
3597
3600
  const VALID_REDEMPTION_RELATIONS = [
3598
- 'redeemCount'
3601
+ 'redeemCount',
3602
+ 'ownerBusiness'
3599
3603
  ];
3600
3604
  /**
3601
3605
  * Type guard to validate redemption include relation at runtime
@@ -3653,6 +3657,26 @@ function isValidUserRelation(value) {
3653
3657
  return VALID_USER_RELATIONS.includes(value);
3654
3658
  }
3655
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
+
3656
3680
  /**
3657
3681
  * Normalizes filter options to use new field names while accepting deprecated ones.
3658
3682
  * Use at controller level to support both old and new API contracts.
@@ -5166,30 +5190,30 @@ class TokenApi {
5166
5190
  return this.apiClient.get(url);
5167
5191
  }
5168
5192
  /**
5169
- * Get all token metadata with filtering and pagination
5193
+ * Get all token metadata with filtering, pagination, and include relations
5170
5194
  *
5171
5195
  * Useful for displaying rewards (ERC1155) or stamps (ERC721) in tables.
5172
5196
  * Non-admin users always get active metadata only.
5173
5197
  *
5174
- * @param options - Filter and pagination options
5198
+ * @param options - Filter, pagination, and include options
5175
5199
  * @returns Paginated response with token metadata
5176
5200
  *
5177
- * @example Get all active rewards
5201
+ * @example Get all active rewards with mint/burn counts
5178
5202
  * ```typescript
5179
5203
  * const rewards = await tokenApi.getTokenMetadata({
5180
5204
  * tokenType: 'ERC1155',
5181
5205
  * active: true,
5182
- * limit: 10
5206
+ * limit: 10,
5207
+ * include: ['mintCount', 'burnCount', 'token']
5183
5208
  * });
5184
5209
  * ```
5185
5210
  *
5186
- * @example Get stamps with search
5211
+ * @example Get stamps with owner business included
5187
5212
  * ```typescript
5188
5213
  * const stamps = await tokenApi.getTokenMetadata({
5189
5214
  * tokenType: 'ERC721',
5190
5215
  * search: 'gold',
5191
- * sortBy: 'name',
5192
- * sortOrder: 'ASC'
5216
+ * include: ['ownerBusiness', 'token']
5193
5217
  * });
5194
5218
  * ```
5195
5219
  */
@@ -5214,6 +5238,14 @@ class TokenApi {
5214
5238
  params.set('tags', tagsArray.join(','));
5215
5239
  }
5216
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
+ }
5217
5249
  const response = await this.apiClient.get(`${this.basePath}/metadata?${params.toString()}`);
5218
5250
  return normalizeToPaginated(response);
5219
5251
  }
@@ -5273,6 +5305,30 @@ class TokenApi {
5273
5305
  async createTokenType(tokenType) {
5274
5306
  return this.apiClient.post(`${this.basePath}/types`, tokenType);
5275
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
+ }
5276
5332
  }
5277
5333
 
5278
5334
  /**
@@ -5327,35 +5383,57 @@ class TokenService {
5327
5383
  return this.tokenApi.getTokenByContractAddress(contractAddress, contractTokenId);
5328
5384
  }
5329
5385
  /**
5330
- * Get all token metadata with filtering and pagination
5386
+ * Get all token metadata with filtering, pagination, and include relations
5331
5387
  *
5332
5388
  * Useful for displaying rewards (ERC1155) or stamps (ERC721) in tables.
5333
5389
  * Non-admin users always get active metadata only.
5334
5390
  *
5335
- * @param options - Filter and pagination options
5391
+ * @param options - Filter, pagination, and include options
5336
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
+ * ```
5337
5401
  */
5338
5402
  async getTokenMetadata(options) {
5339
5403
  return this.tokenApi.getTokenMetadata(options);
5340
5404
  }
5341
5405
  /**
5342
- * Get rewards (ERC1155 token metadata) with filtering and pagination
5406
+ * Get rewards (ERC1155 token metadata) with filtering, pagination, and include relations
5343
5407
  *
5344
5408
  * Convenience method for fetching reward metadata.
5345
5409
  *
5346
- * @param options - Filter and pagination options (tokenType is set automatically)
5410
+ * @param options - Filter, pagination, and include options (tokenType is set automatically)
5347
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
+ * ```
5348
5419
  */
5349
5420
  async getRewards(options) {
5350
5421
  return this.tokenApi.getTokenMetadata({ ...options, tokenType: NativeTokenTypes.ERC1155 });
5351
5422
  }
5352
5423
  /**
5353
- * Get stamps (ERC721 token metadata) with filtering and pagination
5424
+ * Get stamps (ERC721 token metadata) with filtering, pagination, and include relations
5354
5425
  *
5355
5426
  * Convenience method for fetching stamp metadata.
5356
5427
  *
5357
- * @param options - Filter and pagination options (tokenType is set automatically)
5428
+ * @param options - Filter, pagination, and include options (tokenType is set automatically)
5358
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
+ * ```
5359
5437
  */
5360
5438
  async getStamps(options) {
5361
5439
  return this.tokenApi.getTokenMetadata({ ...options, tokenType: NativeTokenTypes.ERC721 });
@@ -5412,6 +5490,17 @@ class TokenService {
5412
5490
  async createTokenType(tokenType) {
5413
5491
  return this.tokenApi.createTokenType(tokenType);
5414
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
+ }
5415
5504
  }
5416
5505
 
5417
5506
  /**
@@ -5514,6 +5603,10 @@ class BusinessApi {
5514
5603
  *
5515
5604
  * // Filter by business type
5516
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' });
5517
5610
  * ```
5518
5611
  */
5519
5612
  async getBusinesses(options) {
@@ -5536,6 +5629,22 @@ class BusinessApi {
5536
5629
  if (options?.businessTypeId !== undefined) {
5537
5630
  params.set('businessTypeId', String(options.businessTypeId));
5538
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
+ }
5539
5648
  const response = await this.apiClient.get(`${this.basePath}?${params.toString()}`);
5540
5649
  return normalizeToPaginated(response);
5541
5650
  }
@@ -7237,6 +7346,12 @@ class RedemptionService {
7237
7346
  async updateRedemptionType(data) {
7238
7347
  return this.redemptionApi.updateRedemptionType(data);
7239
7348
  }
7349
+ /**
7350
+ * ADMIN: Delete redemption
7351
+ */
7352
+ async deleteRedemption(id) {
7353
+ return this.redemptionApi.deleteRedemption(id);
7354
+ }
7240
7355
  /**
7241
7356
  * ADMIN: Delete redemption type
7242
7357
  */
@@ -9172,7 +9287,7 @@ class TriggerSourceApi {
9172
9287
  /**
9173
9288
  * PUBLIC: Get trigger sources with optional filters and pagination
9174
9289
  *
9175
- * @param options - Filter and pagination options
9290
+ * @param options - Filter and pagination options (includes location filters)
9176
9291
  * @returns Paginated list of trigger sources
9177
9292
  *
9178
9293
  * @example
@@ -9186,6 +9301,12 @@ class TriggerSourceApi {
9186
9301
  * page: 1,
9187
9302
  * limit: 20
9188
9303
  * });
9304
+ *
9305
+ * // Filter by location (geocoding)
9306
+ * const dubaiSources = await triggerSourceApi.getTriggerSources({
9307
+ * city: 'Dubai',
9308
+ * countryCode: 'AE'
9309
+ * });
9189
9310
  * ```
9190
9311
  */
9191
9312
  async getTriggerSources(options) {
@@ -9198,6 +9319,19 @@ class TriggerSourceApi {
9198
9319
  params.set('campaignId', options.campaignId);
9199
9320
  if (options?.active !== undefined)
9200
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);
9201
9335
  const response = await this.apiClient.get(`/trigger-sources?${params.toString()}`);
9202
9336
  return normalizeToPaginated(response);
9203
9337
  }
@@ -9782,6 +9916,9 @@ class AuthService {
9782
9916
  * Extracts context (tenantId/businessId) from the current refresh token to ensure
9783
9917
  * the recovered session maintains the same context.
9784
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
+ *
9785
9922
  * @returns true if session was successfully recovered
9786
9923
  */
9787
9924
  async attemptProviderTokenRecovery() {
@@ -9934,6 +10071,8 @@ class TokenRefreshManager {
9934
10071
  // Retry configuration for transient failures
9935
10072
  this.MAX_RETRY_ATTEMPTS = 3;
9936
10073
  this.RETRY_DELAY_MS = 1000;
10074
+ // Active refresh promise for deduplication
10075
+ this.activeRefreshPromise = null;
9937
10076
  }
9938
10077
  /**
9939
10078
  * Ensures the access token is valid, refreshing if needed.
@@ -10034,11 +10173,27 @@ class TokenRefreshManager {
10034
10173
  return new Promise(resolve => setTimeout(resolve, ms));
10035
10174
  }
10036
10175
  /**
10037
- * @deprecated Use ensureValidToken() instead
10176
+ * Attempt token refresh: internal first, then provider token fallback.
10177
+ * DEDUPLICATES concurrent calls - all callers share the same promise.
10038
10178
  */
10039
10179
  async attemptInternalRefresh() {
10040
- const result = await this.attemptRefreshWithRetry();
10041
- return result.success;
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
+ }
10042
10197
  }
10043
10198
  }
10044
10199
 
@@ -10844,7 +10999,7 @@ class DefaultAuthProvider {
10844
10999
  /** SDK package name */
10845
11000
  const SDK_NAME = "@explorins/pers-sdk";
10846
11001
  /** SDK version - injected from package.json at build time */
10847
- const SDK_VERSION = "2.1.37";
11002
+ const SDK_VERSION = "2.1.39";
10848
11003
  /** Full SDK identifier for headers */
10849
11004
  const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
10850
11005
 
@@ -10954,12 +11109,16 @@ class PersApiClient {
10954
11109
  }
10955
11110
  const { retryCount = 0, responseType = 'json', bypassAuth = false } = options || {};
10956
11111
  const url = `${this.apiRoot}${endpoint}`;
10957
- // SMART TOKEN VALIDATION: Only check if we suspect the token might be expired
11112
+ // TOKEN VALIDATION: Ensure token is valid before request
11113
+ // This blocks the request until token validation completes - prevents 401s from expired tokens
10958
11114
  if (!bypassAuth && this.mergedConfig.authProvider && retryCount === 0) {
10959
- // Fire-and-forget validation - don't block the request unless we know there's an issue
10960
- this.ensureValidToken().catch(error => {
10961
- console.debug('[PersApiClient] Background token validation failed:', error);
10962
- });
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
+ }
10963
11122
  }
10964
11123
  const requestOptions = {
10965
11124
  headers: await this.getHeaders(!bypassAuth, method, url),
@@ -10987,49 +11146,63 @@ class PersApiClient {
10987
11146
  return result;
10988
11147
  }
10989
11148
  catch (error) {
10990
- // Error handling - proactive token refresh should prevent most 401s
10991
11149
  const status = ErrorUtils.getStatus(error);
10992
11150
  const errorMessage = ErrorUtils.getMessage(error);
10993
- // Handle 401 errors centrally through AuthService
11151
+ // Handle 401 Unauthorized errors
10994
11152
  if (status === 401) {
10995
11153
  const backendError = ErrorUtils.extractBackendErrorDetails(error);
10996
- // Check for FATAL auth error CODES (not just 401 status)
10997
- // Fatal codes: REFRESH_TOKEN_EXPIRED, TOKEN_REVOKED, etc. → immediate logout
10998
- // Non-fatal codes: TOKEN_EXPIRED → try refresh first
11154
+ // ===========================================
11155
+ // FATAL AUTH ERRORS → Immediate logout
11156
+ // ===========================================
11157
+ // Fatal codes: REFRESH_TOKEN_EXPIRED, TOKEN_REVOKED, INVALID_REFRESH_TOKEN, etc.
10999
11158
  if (this.authService.isFatalAuthError(backendError.code)) {
11000
- // Definitive auth failure - no retry, session is invalid
11159
+ console.warn(`[PersApiClient] Fatal auth error: ${backendError.code}`);
11001
11160
  await this.authService.handleAuthFailure();
11002
11161
  this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
11003
11162
  throw new AuthenticationError(errorMessage, endpoint, method, backendError);
11004
11163
  }
11005
- // If this IS a refresh request that failed with 401, don't retry to avoid infinite loop
11164
+ // ===========================================
11165
+ // REFRESH REQUEST FAILED → Logout (prevents infinite loop)
11166
+ // ===========================================
11006
11167
  if (options?.isRefreshRequest) {
11007
- // Refresh failed - this is a real auth failure
11168
+ console.warn('[PersApiClient] Refresh request itself failed with 401');
11008
11169
  await this.authService.handleAuthFailure();
11009
11170
  this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
11010
11171
  throw new AuthenticationError(errorMessage, endpoint, method, backendError);
11011
11172
  }
11012
- // TOKEN_EXPIRED or unknown 401: try refresh once, then retry
11173
+ // ===========================================
11174
+ // RETRYABLE 401 (TOKEN_EXPIRED) → Try refresh, then retry
11175
+ // ===========================================
11013
11176
  if (retryCount === 0 && this.mergedConfig.authProvider) {
11014
- try {
11015
- const refreshSuccessful = await this.refreshManager.attemptInternalRefresh();
11016
- if (refreshSuccessful) {
11017
- // Emit TOKEN_REFRESHED event so WS clients can retry if they gave up
11018
- this._events?.emitSuccess({
11019
- type: 'token_refreshed',
11020
- domain: 'authentication',
11021
- userMessage: 'Authentication token refreshed',
11022
- });
11023
- // Retry with new token
11024
- return await this.request(method, endpoint, body, { ...options, retryCount: 1 });
11025
- }
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 });
11186
+ }
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();
11026
11192
  }
11027
- catch (refreshError) {
11028
- // Refresh failed - fall through to handleAuthFailure
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');
11029
11196
  }
11197
+ this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
11198
+ throw new AuthenticationError(errorMessage, endpoint, method, backendError);
11030
11199
  }
11031
- // All recovery attempts failed
11032
- await this.authService.handleAuthFailure();
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');
11033
11206
  this.emitErrorEvent(backendError, errorMessage, endpoint, method, status, error);
11034
11207
  throw new AuthenticationError(errorMessage, endpoint, method, backendError);
11035
11208
  }
@@ -12694,16 +12867,17 @@ class TokenManager {
12694
12867
  return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
12695
12868
  }
12696
12869
  /**
12697
- * Get all token metadata with filtering and pagination
12870
+ * Get all token metadata with filtering, pagination, and include relations
12698
12871
  *
12699
- * Retrieves token metadata (rewards/stamps) with server-side filtering and pagination.
12872
+ * Retrieves token metadata (rewards/stamps) with server-side filtering, pagination,
12873
+ * and optional include relations for additional data.
12700
12874
  * Useful for displaying rewards (ERC1155) or stamps (ERC721) in admin tables.
12701
12875
  * Non-admin users always get active metadata only.
12702
12876
  *
12703
- * @param options - Filter and pagination options
12877
+ * @param options - Filter, pagination, and include options
12704
12878
  * @returns Promise resolving to paginated token metadata
12705
12879
  *
12706
- * @example Get paginated rewards
12880
+ * @example Get paginated rewards with counts
12707
12881
  * ```typescript
12708
12882
  * const rewards = await sdk.tokens.getTokenMetadata({
12709
12883
  * tokenType: 'ERC1155',
@@ -12711,19 +12885,26 @@ class TokenManager {
12711
12885
  * page: 1,
12712
12886
  * limit: 10,
12713
12887
  * sortBy: 'name',
12714
- * sortOrder: 'ASC'
12888
+ * sortOrder: 'ASC',
12889
+ * include: ['mintCount', 'burnCount', 'token']
12715
12890
  * });
12716
12891
  *
12717
- * console.log(`Page 1 of ${rewards.pagination.pages}`);
12718
- * rewards.data.forEach(r => console.log(r.name));
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
+ * });
12719
12896
  * ```
12720
12897
  *
12721
- * @example Get stamps with search
12898
+ * @example Get stamps with owner business
12722
12899
  * ```typescript
12723
12900
  * const stamps = await sdk.tokens.getTokenMetadata({
12724
12901
  * tokenType: 'ERC721',
12725
12902
  * search: 'gold',
12726
- * limit: 25
12903
+ * include: ['ownerBusiness', 'token']
12904
+ * });
12905
+ *
12906
+ * stamps.data.forEach(s => {
12907
+ * console.log(`${s.name} owned by ${s.included?.ownerBusiness?.displayName}`);
12727
12908
  * });
12728
12909
  * ```
12729
12910
  */
@@ -12731,31 +12912,27 @@ class TokenManager {
12731
12912
  return this.tokenService.getTokenMetadata(options);
12732
12913
  }
12733
12914
  /**
12734
- * Get rewards (ERC1155 token metadata) with filtering and pagination
12915
+ * Get rewards (ERC1155 token metadata) with filtering, pagination, and include relations
12735
12916
  *
12736
12917
  * Convenience method for fetching reward metadata. Rewards are ERC1155 tokens
12737
12918
  * that represent collectible items, vouchers, or other redeemable rewards.
12738
12919
  *
12739
- * @param options - Filter and pagination options (tokenType is set automatically)
12920
+ * @param options - Filter, pagination, and include options (tokenType is set automatically)
12740
12921
  * @returns Promise resolving to paginated reward metadata
12741
12922
  *
12742
- * @example Get active rewards
12923
+ * @example Get active rewards with mint counts
12743
12924
  * ```typescript
12744
12925
  * const rewards = await sdk.tokens.getRewards({
12745
12926
  * active: true,
12746
- * limit: 10
12927
+ * limit: 10,
12928
+ * include: ['mintCount', 'burnCount', 'token', 'ownerBusiness']
12747
12929
  * });
12748
12930
  *
12749
- * console.log(`Found ${rewards.pagination.total} rewards`);
12750
- * rewards.data.forEach(r => console.log(`${r.name}: ${r.description}`));
12751
- * ```
12752
- *
12753
- * @example Search rewards
12754
- * ```typescript
12755
- * const discountRewards = await sdk.tokens.getRewards({
12756
- * search: 'discount',
12757
- * sortBy: 'name',
12758
- * sortOrder: 'ASC'
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
+ * }
12759
12936
  * });
12760
12937
  * ```
12761
12938
  */
@@ -12763,31 +12940,27 @@ class TokenManager {
12763
12940
  return this.tokenService.getRewards(options);
12764
12941
  }
12765
12942
  /**
12766
- * Get stamps (ERC721 token metadata) with filtering and pagination
12943
+ * Get stamps (ERC721 token metadata) with filtering, pagination, and include relations
12767
12944
  *
12768
12945
  * Convenience method for fetching stamp metadata. Stamps are ERC721 tokens
12769
12946
  * that represent achievements, badges, or collectible status markers.
12770
12947
  *
12771
- * @param options - Filter and pagination options (tokenType is set automatically)
12948
+ * @param options - Filter, pagination, and include options (tokenType is set automatically)
12772
12949
  * @returns Promise resolving to paginated stamp metadata
12773
12950
  *
12774
- * @example Get active stamps
12951
+ * @example Get active stamps with all includes
12775
12952
  * ```typescript
12776
12953
  * const stamps = await sdk.tokens.getStamps({
12777
12954
  * active: true,
12778
- * limit: 10
12955
+ * limit: 10,
12956
+ * include: ['mintCount', 'burnCount', 'token', 'ownerBusiness']
12779
12957
  * });
12780
12958
  *
12781
- * console.log(`Found ${stamps.pagination.total} stamps`);
12782
- * stamps.data.forEach(s => console.log(`${s.name}: ${s.description}`));
12783
- * ```
12784
- *
12785
- * @example Filter stamps by tags
12786
- * ```typescript
12787
- * const goldStamps = await sdk.tokens.getStamps({
12788
- * tags: ['gold', 'premium'],
12789
- * sortBy: 'createdAt',
12790
- * sortOrder: 'DESC'
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
+ * }
12791
12964
  * });
12792
12965
  * ```
12793
12966
  */
@@ -12885,6 +13058,32 @@ class TokenManager {
12885
13058
  async toggleTokenActive(tokenId) {
12886
13059
  return this.tokenService.toggleTokenActive(tokenId);
12887
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
+ }
12888
13087
  /**
12889
13088
  * Get the full token SDK for advanced operations
12890
13089
  *
@@ -15278,6 +15477,21 @@ class RedemptionManager {
15278
15477
  async deleteRedemptionType(id) {
15279
15478
  return this.redemptionService.deleteRedemptionType(id);
15280
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
+ }
15281
15495
  /**
15282
15496
  * Get the full redemption service for advanced operations
15283
15497
  *
@@ -46730,6 +46944,7 @@ exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
46730
46944
  exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
46731
46945
  exports.VALID_REDEMPTION_RELATIONS = VALID_REDEMPTION_RELATIONS;
46732
46946
  exports.VALID_RELATIONS = VALID_RELATIONS;
46947
+ exports.VALID_TOKEN_METADATA_RELATIONS = VALID_TOKEN_METADATA_RELATIONS;
46733
46948
  exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
46734
46949
  exports.VALID_USER_RELATIONS = VALID_USER_RELATIONS;
46735
46950
  exports.WalletEventsManager = WalletEventsManager;
@@ -46781,6 +46996,7 @@ exports.isValidCampaignRelation = isValidCampaignRelation;
46781
46996
  exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
46782
46997
  exports.isValidRedemptionRelation = isValidRedemptionRelation;
46783
46998
  exports.isValidRelation = isValidRelation;
46999
+ exports.isValidTokenMetadataRelation = isValidTokenMetadataRelation;
46784
47000
  exports.isValidTransactionRelation = isValidTransactionRelation;
46785
47001
  exports.isValidUserRelation = isValidUserRelation;
46786
47002
  exports.mergeWithDefaults = mergeWithDefaults;