@explorins/pers-sdk-react-native 2.1.2 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +7 -7
  2. package/dist/hooks/index.d.ts +6 -0
  3. package/dist/hooks/index.d.ts.map +1 -1
  4. package/dist/hooks/index.js +1 -0
  5. package/dist/hooks/useAnalytics.d.ts +37 -14
  6. package/dist/hooks/useAnalytics.d.ts.map +1 -1
  7. package/dist/hooks/useAnalytics.js +239 -19
  8. package/dist/hooks/useCampaigns.d.ts +14 -6
  9. package/dist/hooks/useCampaigns.d.ts.map +1 -1
  10. package/dist/hooks/useCampaigns.js +144 -10
  11. package/dist/hooks/useRedemptions.d.ts +5 -2
  12. package/dist/hooks/useRedemptions.d.ts.map +1 -1
  13. package/dist/hooks/useRedemptions.js +53 -2
  14. package/dist/hooks/useTokenBalances.d.ts.map +1 -1
  15. package/dist/hooks/useTokenBalances.js +21 -8
  16. package/dist/hooks/useTransactions.d.ts +8 -5
  17. package/dist/hooks/useTransactions.d.ts.map +1 -1
  18. package/dist/hooks/useTransactions.js +70 -27
  19. package/dist/hooks/useTriggerSources.d.ts +76 -0
  20. package/dist/hooks/useTriggerSources.d.ts.map +1 -0
  21. package/dist/hooks/useTriggerSources.js +272 -0
  22. package/dist/index.d.ts +12 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2742 -495
  25. package/dist/index.js.map +1 -1
  26. package/dist/providers/PersSDKProvider.d.ts +1 -12
  27. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  28. package/dist/providers/PersSDKProvider.js +50 -25
  29. package/package.json +2 -2
  30. package/src/hooks/index.ts +17 -1
  31. package/src/hooks/useAnalytics.ts +268 -21
  32. package/src/hooks/useCampaigns.ts +176 -14
  33. package/src/hooks/useRedemptions.ts +66 -3
  34. package/src/hooks/useTokenBalances.ts +23 -9
  35. package/src/hooks/useTransactions.ts +84 -29
  36. package/src/hooks/useTriggerSources.ts +301 -0
  37. package/src/index.ts +33 -3
  38. package/src/providers/PersSDKProvider.tsx +58 -39
package/dist/index.js CHANGED
@@ -2828,6 +2828,28 @@ exports.SortOrder = void 0;
2828
2828
  SortOrder["DESC"] = "DESC";
2829
2829
  })(exports.SortOrder || (exports.SortOrder = {}));
2830
2830
 
2831
+ /**
2832
+ * ProcessRecordStatus - Status for business process records that create blockchain transactions
2833
+ *
2834
+ * Used for tracking the lifecycle of business operations (campaigns, redemptions) that
2835
+ * trigger blockchain transactions. This is distinct from TransactionStatus which tracks
2836
+ * the blockchain transaction itself.
2837
+ *
2838
+ * Usage:
2839
+ * - Campaign Claims (user claims rewards via blockchain transactions)
2840
+ * - Redemption Redeems (user redeems items via blockchain transactions)
2841
+ * - Any business process creating blockchain transactions that need status tracking
2842
+ *
2843
+ * NOT for blockchain transactions themselves - use TransactionStatus instead.
2844
+ */
2845
+ exports.ProcessRecordStatus = void 0;
2846
+ (function (ProcessRecordStatus) {
2847
+ ProcessRecordStatus["PENDING"] = "PENDING";
2848
+ ProcessRecordStatus["PROCESSING"] = "PROCESSING";
2849
+ ProcessRecordStatus["COMPLETED"] = "COMPLETED";
2850
+ ProcessRecordStatus["FAILED"] = "FAILED"; // Transaction processing failed (blockchain or validation error)
2851
+ })(exports.ProcessRecordStatus || (exports.ProcessRecordStatus = {}));
2852
+
2831
2853
  exports.PurchaseStatus = void 0;
2832
2854
  (function (PurchaseStatus) {
2833
2855
  // after creation of payment and before payment is done by user
@@ -2847,35 +2869,30 @@ exports.PurchaseCurrency = void 0;
2847
2869
  PurchaseCurrency["EUR"] = "eur";
2848
2870
  })(exports.PurchaseCurrency || (exports.PurchaseCurrency = {}));
2849
2871
 
2850
- exports.RedemptionRedeemStatus = void 0;
2851
- (function (RedemptionRedeemStatus) {
2852
- RedemptionRedeemStatus["PENDING"] = "PENDING";
2853
- RedemptionRedeemStatus["PROCESSING"] = "PROCESSING";
2854
- RedemptionRedeemStatus["COMPLETED"] = "COMPLETED";
2855
- RedemptionRedeemStatus["FAILED"] = "FAILED"; // Processing failed
2856
- })(exports.RedemptionRedeemStatus || (exports.RedemptionRedeemStatus = {}));
2857
-
2858
2872
  /**
2859
2873
  * Trigger Source Types
2860
2874
  * Defines the different types of triggers that can activate a campaign flow
2861
- */
2862
- exports.TriggerSourceType = void 0;
2863
- (function (TriggerSourceType) {
2864
- TriggerSourceType["QR_CODE"] = "QR_CODE";
2865
- // NFC_TAG = 'NFC_TAG',
2866
- TriggerSourceType["API_WEBHOOK"] = "API_WEBHOOK";
2867
- // TRANSACTION = 'TRANSACTION',
2868
- })(exports.TriggerSourceType || (exports.TriggerSourceType = {}));
2875
+ * Using const object pattern for better extensibility without requiring deploys
2876
+ */
2877
+ const TRIGGER_SOURCE_TYPES = {
2878
+ QR_CODE: 'QR_CODE',
2879
+ NFC_TAG: 'NFC_TAG',
2880
+ API_WEBHOOK: 'API_WEBHOOK',
2881
+ GPS_GEOFENCE: 'GPS_GEOFENCE',
2882
+ TRANSACTION: 'TRANSACTION',
2883
+ };
2884
+ // Export values array for validation
2885
+ const TRIGGER_SOURCE_TYPE_VALUES = Object.values(TRIGGER_SOURCE_TYPES);
2869
2886
  /**
2870
2887
  * Source Logic Types
2871
2888
  * Defines how multiple trigger sources combine to activate a flow
2872
2889
  * Currently only ANY (OR logic) is implemented
2873
2890
  */
2874
- exports.SourceLogicType = void 0;
2875
- (function (SourceLogicType) {
2876
- SourceLogicType["ANY"] = "any";
2877
- // Future: ALL = 'all', SEQUENCE = 'sequence', WEIGHTED = 'weighted'
2878
- })(exports.SourceLogicType || (exports.SourceLogicType = {}));
2891
+ const SOURCE_LOGIC_TYPES = {
2892
+ ANY: 'any'
2893
+ // Future: ALL: 'all', SEQUENCE: 'sequence', WEIGHTED: 'weighted'
2894
+ };
2895
+ const SOURCE_LOGIC_TYPE_VALUES = Object.values(SOURCE_LOGIC_TYPES);
2879
2896
 
2880
2897
  exports.CampaignConditionType = void 0;
2881
2898
  (function (CampaignConditionType) {
@@ -3104,6 +3121,103 @@ const Domains = {
3104
3121
  AUDIT: 'audit',
3105
3122
  };
3106
3123
 
3124
+ /**
3125
+ * SQL operator mapping for query operators
3126
+ * Maps abstract query operators to SQL equivalents
3127
+ */
3128
+ const QUERY_OPERATOR_SQL_MAP = {
3129
+ eq: '=',
3130
+ ne: '!=',
3131
+ gt: '>',
3132
+ gte: '>=',
3133
+ lt: '<',
3134
+ lte: '<=',
3135
+ in: 'IN',
3136
+ nin: 'NOT IN'
3137
+ };
3138
+
3139
+ /**
3140
+ * Helper to validate includes array at runtime
3141
+ */
3142
+ const VALID_RELATIONS = ['user', 'campaign', 'business', 'triggerSource'];
3143
+ function isValidRelation(value) {
3144
+ return VALID_RELATIONS.includes(value);
3145
+ }
3146
+
3147
+ /**
3148
+ * Valid transaction relations for runtime validation
3149
+ */
3150
+ const VALID_TRANSACTION_RELATIONS = [
3151
+ 'sender',
3152
+ 'recipient',
3153
+ 'business'
3154
+ ];
3155
+ /**
3156
+ * Type guard to validate relation strings at runtime
3157
+ */
3158
+ function isValidTransactionRelation(value) {
3159
+ return VALID_TRANSACTION_RELATIONS.includes(value);
3160
+ }
3161
+
3162
+ /**
3163
+ * Array of all valid campaign include relations
3164
+ * Used for runtime validation
3165
+ */
3166
+ const VALID_CAMPAIGN_RELATIONS = [
3167
+ 'triggerSources',
3168
+ 'businesses'
3169
+ ];
3170
+ /**
3171
+ * Type guard to validate campaign include relation at runtime
3172
+ *
3173
+ * @param value - String to validate
3174
+ * @returns True if value is a valid CampaignIncludeRelation
3175
+ */
3176
+ function isValidCampaignRelation(value) {
3177
+ return VALID_CAMPAIGN_RELATIONS.includes(value);
3178
+ }
3179
+
3180
+ /**
3181
+ * Array of all valid campaign claim include relations
3182
+ * Used for runtime validation
3183
+ */
3184
+ const VALID_CAMPAIGN_CLAIM_RELATIONS = [
3185
+ 'campaign',
3186
+ 'user',
3187
+ 'business',
3188
+ 'triggerSource',
3189
+ 'transactions'
3190
+ ];
3191
+ /**
3192
+ * Type guard to validate campaign claim include relation at runtime
3193
+ *
3194
+ * @param value - String to validate
3195
+ * @returns True if value is a valid CampaignClaimIncludeRelation
3196
+ */
3197
+ function isValidCampaignClaimRelation(value) {
3198
+ return VALID_CAMPAIGN_CLAIM_RELATIONS.includes(value);
3199
+ }
3200
+
3201
+ /**
3202
+ * Array of all valid redemption redeem include relations
3203
+ * Used for runtime validation
3204
+ */
3205
+ const VALID_REDEMPTION_REDEEM_RELATIONS = [
3206
+ 'redemption',
3207
+ 'user',
3208
+ 'business',
3209
+ 'transactions'
3210
+ ];
3211
+ /**
3212
+ * Type guard to validate redemption redeem include relation at runtime
3213
+ *
3214
+ * @param value - String to validate
3215
+ * @returns True if value is a valid RedemptionRedeemIncludeRelation
3216
+ */
3217
+ function isValidRedemptionRedeemRelation(value) {
3218
+ return VALID_REDEMPTION_REDEEM_RELATIONS.includes(value);
3219
+ }
3220
+
3107
3221
  /**
3108
3222
  * Transaction format constants for Ethereum and EVM-compatible chains
3109
3223
  * Using const assertions for zero runtime overhead
@@ -3642,10 +3756,30 @@ class UserApi {
3642
3756
  * ADMIN: Get all remote users with query parameters
3643
3757
  * Uses new RESTful /users endpoint with role-based access
3644
3758
  * Note: Admin users get full data, non-admin users get public profiles only
3759
+ *
3760
+ * @param options - Pagination options (page, limit, sortBy, sortOrder)
3761
+ * @param search - Optional search query to filter users
3762
+ *
3763
+ * @example
3764
+ * ```typescript
3765
+ * // Get users sorted by email
3766
+ * const users = await sdk.users.getAllUsers({
3767
+ * page: 1,
3768
+ * limit: 10,
3769
+ * sortBy: 'email',
3770
+ * sortOrder: SortOrder.ASC
3771
+ * });
3772
+ *
3773
+ * // Search for users
3774
+ * const searchResults = await sdk.users.getAllUsers({ page: 1, limit: 10 }, 'john');
3775
+ * ```
3645
3776
  */
3646
- async getAllRemoteUsers(options) {
3777
+ async getAllRemoteUsers(options, search) {
3647
3778
  const params = buildPaginationParams(options);
3648
- const url = `${this.basePath}?merge=soft${params ? '&' + params.toString() : ''}`;
3779
+ if (search) {
3780
+ params.set('search', search);
3781
+ }
3782
+ const url = `${this.basePath}?merge=soft&${params.toString()}`;
3649
3783
  const response = await this.apiClient.get(url);
3650
3784
  return normalizeToPaginated(response);
3651
3785
  }
@@ -3740,9 +3874,11 @@ class UserService {
3740
3874
  /**
3741
3875
  * ADMIN: Get all remote users
3742
3876
  * ✅ FIXED: Matches API method signature (no parameters needed)
3877
+ * @param options - Pagination options
3878
+ * @param search - Optional search query
3743
3879
  */
3744
- async getAllRemoteUsers(options) {
3745
- return this.userApi.getAllRemoteUsers(options);
3880
+ async getAllRemoteUsers(options, search) {
3881
+ return this.userApi.getAllRemoteUsers(options, search);
3746
3882
  }
3747
3883
  /**
3748
3884
  * ADMIN: Update user as admin
@@ -4913,6 +5049,19 @@ class CampaignApi {
4913
5049
  * Get campaigns with pagination support
4914
5050
  * Returns paginated response with metadata (total, page, limit, hasMore)
4915
5051
  * Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all
5052
+ *
5053
+ * @param options - Filter, pagination, and include options
5054
+ * @param options.include - Relations to include: 'triggerSources', 'businesses'
5055
+ *
5056
+ * @example
5057
+ * ```typescript
5058
+ * // Get campaigns with trigger sources included
5059
+ * const campaigns = await api.getCampaigns({ include: ['triggerSources'] });
5060
+ * campaigns.data.forEach(c => console.log(c.included?.triggerSources));
5061
+ *
5062
+ * // Get campaigns with both relations
5063
+ * const full = await api.getCampaigns({ include: ['triggerSources', 'businesses'] });
5064
+ * ```
4916
5065
  */
4917
5066
  async getCampaigns(options) {
4918
5067
  const params = [];
@@ -4929,14 +5078,28 @@ class CampaignApi {
4929
5078
  params.push(`sortBy=${options.sortBy}`);
4930
5079
  if (options?.sortOrder)
4931
5080
  params.push(`sortOrder=${options.sortOrder}`);
5081
+ if (options?.include?.length)
5082
+ params.push(`include=${options.include.join(',')}`);
4932
5083
  return this.apiClient.get(`/campaigns?${params.join('&')}`);
4933
5084
  }
4934
5085
  /**
4935
5086
  * PUBLIC: Get campaign by ID
4936
5087
  * NEW: /campaigns/{id}
5088
+ *
5089
+ * @param id - Campaign UUID
5090
+ * @param include - Relations to include: 'triggerSources', 'businesses'
5091
+ *
5092
+ * @example
5093
+ * ```typescript
5094
+ * // Get campaign with all relations
5095
+ * const campaign = await api.getCampaignById('123', ['triggerSources', 'businesses']);
5096
+ * console.log(campaign.included?.triggerSources);
5097
+ * console.log(campaign.included?.businesses);
5098
+ * ```
4937
5099
  */
4938
- async getCampaignById(id) {
4939
- return this.apiClient.get(`/campaigns/${id}`);
5100
+ async getCampaignById(id, include) {
5101
+ const params = include?.length ? `?include=${include.join(',')}` : '';
5102
+ return this.apiClient.get(`/campaigns/${id}${params}`);
4940
5103
  }
4941
5104
  /**
4942
5105
  * ADMIN: Create campaign
@@ -5055,6 +5218,16 @@ class CampaignApi {
5055
5218
  const response = await this.apiClient.get(`/campaign-triggers?${params.toString()}`);
5056
5219
  return normalizeToPaginated(response);
5057
5220
  }
5221
+ /**
5222
+ * PUBLIC: Get campaign trigger by ID
5223
+ * NEW: GET /campaign-triggers/{id}
5224
+ *
5225
+ * @param triggerId - Campaign trigger UUID
5226
+ * @returns Campaign trigger details
5227
+ */
5228
+ async getCampaignTriggerById(triggerId) {
5229
+ return this.apiClient.get(`/campaign-triggers/${triggerId}`);
5230
+ }
5058
5231
  /**
5059
5232
  * ADMIN: Create campaign trigger
5060
5233
  * NEW: POST /campaign-triggers
@@ -5083,6 +5256,13 @@ class CampaignApi {
5083
5256
  async setCampaignTrigger(campaignId, triggerId) {
5084
5257
  return this.apiClient.put(`/campaign-triggers/${triggerId}/assign/${campaignId}`, {});
5085
5258
  }
5259
+ /**
5260
+ * ADMIN: Remove trigger from campaign (unassign)
5261
+ * NEW: DELETE /campaign-triggers/{triggerId}/assign/{campaignId}
5262
+ */
5263
+ async removeCampaignTrigger(campaignId, triggerId) {
5264
+ return this.apiClient.delete(`/campaign-triggers/${triggerId}/assign/${campaignId}`);
5265
+ }
5086
5266
  /**
5087
5267
  * ADMIN: Add/Remove condition to trigger
5088
5268
  * NEW: PUT /campaign-triggers/{triggerId}/conditions/{conditionId}
@@ -5154,7 +5334,7 @@ class CampaignApi {
5154
5334
  * });
5155
5335
  * ```
5156
5336
  */
5157
- async getClaims(filters) {
5337
+ async getClaims(filters, include) {
5158
5338
  const params = buildPaginationParams(filters);
5159
5339
  // Add filter parameters
5160
5340
  if (filters?.campaignId)
@@ -5163,6 +5343,10 @@ class CampaignApi {
5163
5343
  params.set('userId', filters.userId);
5164
5344
  if (filters?.businessId)
5165
5345
  params.set('businessId', filters.businessId);
5346
+ // Add include parameter
5347
+ if (include && include.length > 0) {
5348
+ include.forEach(relation => params.append('include', relation));
5349
+ }
5166
5350
  const endpoint = `/campaigns/claims?${params.toString()}`;
5167
5351
  const response = await this.apiClient.get(endpoint);
5168
5352
  return normalizeToPaginated(response);
@@ -5172,13 +5356,42 @@ class CampaignApi {
5172
5356
  * Uses /me endpoint for authenticated user context
5173
5357
  *
5174
5358
  * @param options - Pagination options
5359
+ * @param include - Optional relations to include (campaign, user, business)
5175
5360
  * @returns Paginated list of user's claims
5176
5361
  */
5177
- async getClaimsLoggedUser(options) {
5362
+ async getClaimsLoggedUser(options, include) {
5178
5363
  const params = buildPaginationParams(options);
5364
+ // Add include parameter
5365
+ if (include && include.length > 0) {
5366
+ include.forEach(relation => params.append('include', relation));
5367
+ }
5179
5368
  const response = await this.apiClient.get(`/campaigns/claims/me?${params.toString()}`);
5180
5369
  return normalizeToPaginated(response);
5181
5370
  }
5371
+ // ==========================================
5372
+ // TRIGGER SOURCE ASSIGNMENT (/campaigns/{id}/trigger-sources)
5373
+ // Note: TriggerSource CRUD is in TriggerSourceApi
5374
+ // ==========================================
5375
+ /**
5376
+ * ADMIN: Assign a trigger source to a campaign
5377
+ *
5378
+ * @param campaignId - Campaign UUID
5379
+ * @param triggerSourceId - Trigger source UUID
5380
+ * @returns Updated campaign with trigger source assigned
5381
+ */
5382
+ async assignTriggerSourceToCampaign(campaignId, triggerSourceId) {
5383
+ return this.apiClient.put(`/campaigns/${campaignId}/trigger-sources/${triggerSourceId}`, {});
5384
+ }
5385
+ /**
5386
+ * ADMIN: Remove a trigger source from a campaign
5387
+ *
5388
+ * @param campaignId - Campaign UUID
5389
+ * @param triggerSourceId - Trigger source UUID
5390
+ * @returns Updated campaign with trigger source removed
5391
+ */
5392
+ async removeTriggerSourceFromCampaign(campaignId, triggerSourceId) {
5393
+ return this.apiClient.delete(`/campaigns/${campaignId}/trigger-sources/${triggerSourceId}`);
5394
+ }
5182
5395
  }
5183
5396
 
5184
5397
  /**
@@ -5198,9 +5411,11 @@ class CampaignService {
5198
5411
  // ==========================================
5199
5412
  /**
5200
5413
  * PUBLIC: Get campaign by ID
5414
+ * @param id - Campaign UUID
5415
+ * @param include - Relations to include: 'triggerSources', 'businesses'
5201
5416
  */
5202
- async getCampaignById(id) {
5203
- return this.campaignApi.getCampaignById(id);
5417
+ async getCampaignById(id, include) {
5418
+ return this.campaignApi.getCampaignById(id, include);
5204
5419
  }
5205
5420
  // ==========================================
5206
5421
  // AUTHENTICATED OPERATIONS
@@ -5213,9 +5428,11 @@ class CampaignService {
5213
5428
  }
5214
5429
  /**
5215
5430
  * AUTH: Get claims for logged user with pagination
5431
+ * @param options - Pagination options
5432
+ * @param include - Optional relations to include (campaign, user, business)
5216
5433
  */
5217
- async getClaimsForLoggedUser(options) {
5218
- return this.campaignApi.getClaimsLoggedUser(options);
5434
+ async getClaimsForLoggedUser(options, include) {
5435
+ return this.campaignApi.getClaimsLoggedUser(options, include);
5219
5436
  }
5220
5437
  // ==========================================
5221
5438
  // CAMPAIGN OPERATIONS (Paginated)
@@ -5229,6 +5446,7 @@ class CampaignService {
5229
5446
  * @param options.limit - Items per page (default: 50)
5230
5447
  * @param options.sortBy - Sort field
5231
5448
  * @param options.sortOrder - Sort direction
5449
+ * @param options.include - Relations to include: 'triggerSources', 'businesses'
5232
5450
  */
5233
5451
  async getCampaigns(options) {
5234
5452
  return this.campaignApi.getCampaigns(options);
@@ -5239,6 +5457,30 @@ class CampaignService {
5239
5457
  async getCampaignTriggers(options) {
5240
5458
  return this.campaignApi.getCampaignTriggers(options);
5241
5459
  }
5460
+ /**
5461
+ * ADMIN: Get campaign trigger by ID
5462
+ */
5463
+ async getCampaignTriggerById(triggerId) {
5464
+ return this.campaignApi.getCampaignTriggerById(triggerId);
5465
+ }
5466
+ /**
5467
+ * ADMIN: Create campaign trigger
5468
+ */
5469
+ async createCampaignTrigger(data) {
5470
+ return this.campaignApi.createCampaignTrigger(data);
5471
+ }
5472
+ /**
5473
+ * ADMIN: Update campaign trigger
5474
+ */
5475
+ async updateCampaignTrigger(triggerId, data) {
5476
+ return this.campaignApi.updateCampaignTrigger(triggerId, data);
5477
+ }
5478
+ /**
5479
+ * ADMIN: Delete campaign trigger
5480
+ */
5481
+ async deleteCampaignTrigger(triggerId) {
5482
+ return this.campaignApi.deleteCampaignTrigger(triggerId);
5483
+ }
5242
5484
  /**
5243
5485
  * ADMIN: Toggle campaign active status
5244
5486
  */
@@ -5263,6 +5505,12 @@ class CampaignService {
5263
5505
  async setCampaignTrigger(campaignId, triggerId) {
5264
5506
  return this.campaignApi.setCampaignTrigger(campaignId, triggerId);
5265
5507
  }
5508
+ /**
5509
+ * ADMIN: Remove trigger from campaign (unassign)
5510
+ */
5511
+ async removeCampaignTrigger(campaignId, triggerId) {
5512
+ return this.campaignApi.removeCampaignTrigger(campaignId, triggerId);
5513
+ }
5266
5514
  /**
5267
5515
  * ADMIN: Update campaign
5268
5516
  */
@@ -5298,21 +5546,45 @@ class CampaignService {
5298
5546
  }
5299
5547
  /**
5300
5548
  * ADMIN: Get campaign claims with optional filters and pagination
5549
+ * @param filters - Filter and pagination options
5550
+ * @param include - Optional relations to include (campaign, user, business)
5301
5551
  */
5302
- async getCampaignClaims(filters) {
5303
- return this.campaignApi.getClaims(filters);
5552
+ async getCampaignClaims(filters, include) {
5553
+ return this.campaignApi.getClaims(filters, include);
5304
5554
  }
5305
5555
  /**
5306
5556
  * ADMIN: Get campaign claims by user ID with pagination
5557
+ * @param userId - User ID to filter by
5558
+ * @param options - Pagination options
5559
+ * @param include - Optional relations to include (campaign, user, business)
5307
5560
  */
5308
- async getCampaignClaimsByUserId(userId, options) {
5309
- return this.campaignApi.getClaims({ userId, ...options });
5561
+ async getCampaignClaimsByUserId(userId, options, include) {
5562
+ return this.campaignApi.getClaims({ userId, ...options }, include);
5310
5563
  }
5311
5564
  /**
5312
5565
  * ADMIN: Get campaign claims by business ID with pagination
5566
+ * @param businessId - Business ID to filter by
5567
+ * @param options - Pagination options
5568
+ * @param include - Optional relations to include (campaign, user, business)
5569
+ */
5570
+ async getCampaignClaimsByBusinessId(businessId, options, include) {
5571
+ return this.campaignApi.getClaims({ businessId, ...options }, include);
5572
+ }
5573
+ // ==========================================
5574
+ // TRIGGER SOURCE ASSIGNMENT
5575
+ // Note: TriggerSource CRUD is in TriggerSourceService
5576
+ // ==========================================
5577
+ /**
5578
+ * ADMIN: Assign a trigger source to a campaign
5313
5579
  */
5314
- async getCampaignClaimsByBusinessId(businessId, options) {
5315
- return this.campaignApi.getClaims({ businessId, ...options });
5580
+ async assignTriggerSourceToCampaign(campaignId, triggerSourceId) {
5581
+ return this.campaignApi.assignTriggerSourceToCampaign(campaignId, triggerSourceId);
5582
+ }
5583
+ /**
5584
+ * ADMIN: Remove a trigger source from a campaign
5585
+ */
5586
+ async removeTriggerSourceFromCampaign(campaignId, triggerSourceId) {
5587
+ return this.campaignApi.removeTriggerSourceFromCampaign(campaignId, triggerSourceId);
5316
5588
  }
5317
5589
  }
5318
5590
 
@@ -5344,12 +5616,7 @@ class RedemptionApi {
5344
5616
  * - Public users: Get active redemptions only
5345
5617
  * - Admin users: Get all redemptions with optional filtering
5346
5618
  *
5347
- * @param options.active - Filter by active status (undefined = all for admins/active for public)
5348
- * @param options.adminAccess - Force admin access (requires admin auth)
5349
- * @param options.page - Page number (default: 1)
5350
- * @param options.limit - Items per page (default: 50)
5351
- * @param options.sortBy - Sort field
5352
- * @param options.sortOrder - Sort order (asc/desc)
5619
+ * @param options - Filter and pagination options (active, adminAccess, page, limit, sortBy, sortOrder)
5353
5620
  * @returns Paginated response with redemptions
5354
5621
  */
5355
5622
  async getRedemptions(options) {
@@ -5416,17 +5683,11 @@ class RedemptionApi {
5416
5683
  * - Users: See only their own redeems (userId/businessId filters ignored)
5417
5684
  * - Admins: Can filter by userId, businessId, or redemptionId
5418
5685
  *
5419
- * @param filters.redemptionId - Filter by specific redemption
5420
- * @param filters.userId - Admin only: Filter by user ID
5421
- * @param filters.businessId - Admin only: Filter by business ID
5422
- * @param filters.myRedeems - Force user's own redeems (uses /me endpoint)
5423
- * @param filters.page - Page number (default: 1)
5424
- * @param filters.limit - Items per page (default: 50)
5425
- * @param filters.sortBy - Sort field
5426
- * @param filters.sortOrder - Sort order (asc/desc)
5686
+ * @param filters - Filter and pagination options (redemptionId, userId, businessId, myRedeems, page, limit, sortBy, sortOrder)
5687
+ * @param include - Optional relations to include (redemption, user, business)
5427
5688
  * @returns Paginated response with redemption redeems
5428
5689
  */
5429
- async getRedemptionRedeems(filters) {
5690
+ async getRedemptionRedeems(filters, include) {
5430
5691
  let url = `${this.basePath}/redeems`;
5431
5692
  const params = buildPaginationParams(filters);
5432
5693
  // Use convenience endpoint for user's own redeems
@@ -5435,6 +5696,10 @@ class RedemptionApi {
5435
5696
  if (filters?.redemptionId) {
5436
5697
  params.append('redemptionId', filters.redemptionId);
5437
5698
  }
5699
+ // Add include parameter
5700
+ if (include && include.length > 0) {
5701
+ include.forEach(relation => params.append('include', relation));
5702
+ }
5438
5703
  const response = await this.apiClient.get(`${url}?${params.toString()}`);
5439
5704
  return normalizeToPaginated(response);
5440
5705
  }
@@ -5445,14 +5710,26 @@ class RedemptionApi {
5445
5710
  params.append('userId', filters.userId);
5446
5711
  if (filters?.businessId)
5447
5712
  params.append('businessId', filters.businessId);
5713
+ // Add include parameter
5714
+ if (include && include.length > 0) {
5715
+ include.forEach(relation => params.append('include', relation));
5716
+ }
5448
5717
  const response = await this.apiClient.get(`${url}?${params.toString()}`);
5449
5718
  return normalizeToPaginated(response);
5450
5719
  }
5451
5720
  /**
5452
5721
  * UNIFIED: Get specific redemption redeem by ID
5722
+ * @param id - Redemption redeem ID
5723
+ * @param include - Optional relations to include (redemption, user, business)
5453
5724
  */
5454
- async getRedemptionRedeemById(id) {
5455
- return this.apiClient.get(`${this.basePath}/redeems/${id}`);
5725
+ async getRedemptionRedeemById(id, include) {
5726
+ const params = new URLSearchParams();
5727
+ // Add include parameter
5728
+ if (include && include.length > 0) {
5729
+ include.forEach(relation => params.append('include', relation));
5730
+ }
5731
+ const url = `${this.basePath}/redeems/${id}${params.toString() ? `?${params.toString()}` : ''}`;
5732
+ return this.apiClient.get(url);
5456
5733
  }
5457
5734
  // ==========================================
5458
5735
  // USER OPERATIONS (JWT + Project Key)
@@ -5463,10 +5740,11 @@ class RedemptionApi {
5463
5740
  * Uses convenience endpoint /redemption-redeems/me with optional filtering
5464
5741
  * @param redemptionId - Optional filter by specific redemption
5465
5742
  * @param options - Pagination options (page, limit, sortBy, sortOrder)
5743
+ * @param include - Optional relations to include (redemption, user, business)
5466
5744
  * @returns Paginated response with user's redemption redeems
5467
5745
  */
5468
- async getUserRedeems(redemptionId, options) {
5469
- return this.getRedemptionRedeems({ myRedeems: true, redemptionId, ...options });
5746
+ async getUserRedeems(redemptionId, options, include) {
5747
+ return this.getRedemptionRedeems({ myRedeems: true, redemptionId, ...options }, include);
5470
5748
  }
5471
5749
  // ==========================================
5472
5750
  // ADMIN OPERATIONS (Tenant Admin JWT)
@@ -5599,15 +5877,19 @@ class RedemptionService {
5599
5877
  }
5600
5878
  /**
5601
5879
  * UNIFIED: Get redemption redeems with filtering and pagination
5880
+ * @param filters - Filter options
5881
+ * @param include - Optional relations to include (redemption, user, business)
5602
5882
  */
5603
- async getRedemptionRedeems(filters) {
5604
- return this.redemptionApi.getRedemptionRedeems(filters);
5883
+ async getRedemptionRedeems(filters, include) {
5884
+ return this.redemptionApi.getRedemptionRedeems(filters, include);
5605
5885
  }
5606
5886
  /**
5607
5887
  * Convenience: Get user redemptions with pagination
5888
+ * @param options - Pagination options
5889
+ * @param include - Optional relations to include (redemption, user, business)
5608
5890
  */
5609
- async getUserRedeems(options) {
5610
- return this.redemptionApi.getUserRedeems(undefined, options);
5891
+ async getUserRedeems(options, include) {
5892
+ return this.redemptionApi.getUserRedeems(undefined, options, include);
5611
5893
  }
5612
5894
  // ==========================================
5613
5895
  // ADMIN OPERATIONS
@@ -5672,9 +5954,19 @@ class TransactionApi {
5672
5954
  * Get transaction by ID (public endpoint)
5673
5955
  *
5674
5956
  * UPDATED: /transaction/{id} → /transactions/{id}
5957
+ * UPDATED: Added support for include parameter to enrich with sender/recipient/business entities
5958
+ *
5959
+ * @param transactionId - Transaction ID
5960
+ * @param include - Optional relations to include (sender, recipient, business)
5675
5961
  */
5676
- async getTransactionById(transactionId) {
5677
- return this.apiClient.get(`${this.basePath}/${transactionId}`);
5962
+ async getTransactionById(transactionId, include) {
5963
+ let url = `${this.basePath}/${transactionId}`;
5964
+ if (include && include.length > 0) {
5965
+ const params = new URLSearchParams();
5966
+ include.forEach(relation => params.append('include', relation));
5967
+ url += `?${params.toString()}`;
5968
+ }
5969
+ return this.apiClient.get(url);
5678
5970
  }
5679
5971
  /**
5680
5972
  * Unique method to create a transaction
@@ -5688,13 +5980,78 @@ class TransactionApi {
5688
5980
  // ==========================================
5689
5981
  // AUTHENTICATED USER OPERATIONS
5690
5982
  // ==========================================
5983
+ /**
5984
+ * Helper to build query params from TransactionQueryOptions
5985
+ * Handles all filter options and include parameter
5986
+ */
5987
+ buildTransactionQueryParams(options) {
5988
+ const params = buildPaginationParams(options);
5989
+ if (!options)
5990
+ return params;
5991
+ // Search and identification filters
5992
+ if (options.search)
5993
+ params.append('search', options.search);
5994
+ if (options.participantId)
5995
+ params.append('participantId', options.participantId);
5996
+ if (options.participantAddress)
5997
+ params.append('participantAddress', options.participantAddress);
5998
+ // Business and process filters
5999
+ if (options.engagedBusinessId)
6000
+ params.append('engagedBusinessId', options.engagedBusinessId);
6001
+ if (options.businessScopeId)
6002
+ params.append('businessScopeId', options.businessScopeId);
6003
+ if (options.triggerProcessId)
6004
+ params.append('triggerProcessId', options.triggerProcessId);
6005
+ // Token filters
6006
+ if (options.tokenAddress)
6007
+ params.append('tokenAddress', options.tokenAddress);
6008
+ if (options.tokenType) {
6009
+ if (Array.isArray(options.tokenType)) {
6010
+ options.tokenType.forEach(type => params.append('tokenType', type));
6011
+ }
6012
+ else {
6013
+ params.append('tokenType', options.tokenType);
6014
+ }
6015
+ }
6016
+ // Transaction classification filters (arrays or single values)
6017
+ if (options.role) {
6018
+ if (Array.isArray(options.role)) {
6019
+ options.role.forEach(role => params.append('role', role));
6020
+ }
6021
+ else {
6022
+ params.append('role', options.role);
6023
+ }
6024
+ }
6025
+ if (options.status) {
6026
+ if (Array.isArray(options.status)) {
6027
+ options.status.forEach(status => params.append('status', status));
6028
+ }
6029
+ else {
6030
+ params.append('status', options.status);
6031
+ }
6032
+ }
6033
+ if (options.type) {
6034
+ if (Array.isArray(options.type)) {
6035
+ options.type.forEach(type => params.append('type', type));
6036
+ }
6037
+ else {
6038
+ params.append('type', options.type);
6039
+ }
6040
+ }
6041
+ // Include relations for entity enrichment
6042
+ if (options.include && options.include.length > 0) {
6043
+ options.include.forEach(relation => params.append('include', relation));
6044
+ }
6045
+ return params;
6046
+ }
5691
6047
  /**
5692
6048
  * AUTH: Get user transaction history with role-based filtering and pagination
5693
6049
  *
5694
- * UPDATED: Uses consolidated /transactions/me endpoint with role parameter
5695
- * FIXED: Now correctly returns paginated response (was incorrectly unwrapping to array)
6050
+ * UPDATED: Uses consolidated /transactions/me endpoint with all filter options
6051
+ * UPDATED: Added support for include parameter to enrich with sender/recipient/business entities
6052
+ * UPDATED: Now supports all TransactionFiltersDTO filters (triggerProcessId, status, type, etc.)
5696
6053
  *
5697
- * @param options - Pagination and filter options
6054
+ * @param options - Pagination, filter, and include options
5698
6055
  * @returns Paginated list of user's transactions
5699
6056
  *
5700
6057
  * @example
@@ -5702,24 +6059,30 @@ class TransactionApi {
5702
6059
  * // Get first page of all transactions
5703
6060
  * const page1 = await transactionApi.getUserTransactionHistory();
5704
6061
  *
5705
- * // Filter by role
5706
- * const sent = await transactionApi.getUserTransactionHistory({
6062
+ * // Filter by role and status
6063
+ * const completed = await transactionApi.getUserTransactionHistory({
5707
6064
  * role: TransactionRole.SENDER,
6065
+ * status: TransactionStatus.COMPLETED,
5708
6066
  * page: 1,
5709
6067
  * limit: 50
5710
6068
  * });
5711
6069
  *
5712
- * // Access data and pagination
5713
- * console.log(`Showing ${page1.data.length} of ${page1.pagination.total} transactions`);
6070
+ * // Filter by trigger process (e.g., from campaign claim or redemption)
6071
+ * const claimTransactions = await transactionApi.getUserTransactionHistory({
6072
+ * triggerProcessId: 'claim-abc123',
6073
+ * include: ['sender', 'recipient', 'business']
6074
+ * });
6075
+ *
6076
+ * // Access included entities
6077
+ * claimTransactions.data.forEach(tx => {
6078
+ * if (tx.included?.engagedBusiness) {
6079
+ * console.log('Business:', tx.included.engagedBusiness.displayName);
6080
+ * }
6081
+ * });
5714
6082
  * ```
5715
6083
  */
5716
6084
  async getUserTransactionHistory(options) {
5717
- const params = buildPaginationParams(options);
5718
- // Add role parameter if specified
5719
- if (options?.role) {
5720
- params.set('role', options.role);
5721
- }
5722
- // Backend already returns PaginatedResponseDTO - return it directly
6085
+ const params = this.buildTransactionQueryParams(options);
5723
6086
  return this.apiClient.get(`${this.basePath}/me?${params.toString()}`);
5724
6087
  }
5725
6088
  /**
@@ -5769,45 +6132,17 @@ class TransactionApi {
5769
6132
  async prepareClientSignedTransaction(request) {
5770
6133
  return this.apiClient.post(`${this.basePath}`, request);
5771
6134
  }
5772
- /**
5773
- * ADMIN: Get all tenant transactions with pagination and filtering
5774
- *
5775
- * UPDATED: /transaction/admin → /transactions
5776
- * FIXED: Now correctly returns paginated response (was incorrectly unwrapping to array)
5777
- *
5778
- * @param options - Pagination and filter options
5779
- * @returns Paginated list of tenant transactions
5780
- *
5781
- * @example
5782
- * ```typescript
5783
- * // Get first page
5784
- * const page1 = await transactionApi.getTenantTransactions();
5785
- *
5786
- * // Get specific page with custom limit
5787
- * const page2 = await transactionApi.getTenantTransactions({
5788
- * page: 2,
5789
- * limit: 100
5790
- * });
5791
- *
5792
- * // Access data
5793
- * page1.data.forEach(tx => console.log(tx.id));
5794
- * ```
5795
- *
5796
- * @deprecated Consider using getPaginatedTransactions() for more advanced filtering
5797
- */
5798
- async getTenantTransactions(options) {
5799
- const params = buildPaginationParams(options);
5800
- // Backend already returns PaginatedResponseDTO - return it directly
5801
- return this.apiClient.get(`${this.basePath}?${params.toString()}`);
5802
- }
5803
6135
  /**
5804
6136
  * ADMIN: Get paginated transactions with filtering and sorting
5805
6137
  *
5806
6138
  * UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
6139
+ *
6140
+ * @param options - Pagination request with filters and optional include relations
5807
6141
  */
5808
- async getPaginatedTransactions(params) {
5809
- const queryString = this.buildQueryParams(params).toString();
5810
- return this.apiClient.get(`${this.basePath}?${queryString}`);
6142
+ async getPaginatedTransactions(options) {
6143
+ const queryString = this.buildQueryParams(options).toString();
6144
+ const fullUrl = `${this.basePath}?${queryString}`;
6145
+ return this.apiClient.get(fullUrl);
5811
6146
  }
5812
6147
  /**
5813
6148
  * ADMIN: Export transactions to CSV
@@ -5861,7 +6196,7 @@ class TransactionApi {
5861
6196
  }
5862
6197
  /**
5863
6198
  * Helper to convert DTO object to URLSearchParams
5864
- * Handles nested 'filters' object and arrays correctly.
6199
+ * Handles nested 'filters' object, arrays, and include parameter correctly.
5865
6200
  */
5866
6201
  buildQueryParams(params) {
5867
6202
  const query = new URLSearchParams();
@@ -5874,7 +6209,11 @@ class TransactionApi {
5874
6209
  query.append('sortBy', params.sortBy);
5875
6210
  if (params.sortOrder)
5876
6211
  query.append('sortOrder', params.sortOrder);
5877
- // 2. Handle Nested Filters
6212
+ // 2. Handle Include Relations (root level)
6213
+ if (params.include && params.include.length > 0) {
6214
+ params.include.forEach((relation) => query.append('include', relation));
6215
+ }
6216
+ // 3. Handle Nested Filters
5878
6217
  if (params.filters) {
5879
6218
  Object.entries(params.filters).forEach(([key, value]) => {
5880
6219
  // Skip undefined/null values
@@ -5912,9 +6251,12 @@ class TransactionService {
5912
6251
  }
5913
6252
  /**
5914
6253
  * Get transaction by ID
6254
+ *
6255
+ * @param transactionId - Transaction ID
6256
+ * @param include - Optional relations to include (sender, recipient, business)
5915
6257
  */
5916
- async getTransactionById(transactionId) {
5917
- return this.transactionApi.getTransactionById(transactionId);
6258
+ async getTransactionById(transactionId, include) {
6259
+ return this.transactionApi.getTransactionById(transactionId, include);
5918
6260
  }
5919
6261
  // ==========================================
5920
6262
  // AUTHENTICATED OPERATIONS
@@ -5932,14 +6274,13 @@ class TransactionService {
5932
6274
  return this.transactionApi.submitSignedTransaction(signedTxData);
5933
6275
  }
5934
6276
  /**
5935
- * AUTH: Get user transaction history by type with pagination
6277
+ * AUTH: Get user transaction history with comprehensive filtering
5936
6278
  *
5937
- * @param role - Optional transaction role filter
5938
- * @param options - Pagination options
6279
+ * @param options - Query options including filters, pagination, and include relations
5939
6280
  * @returns Paginated transaction history
5940
6281
  */
5941
- async getUserTransactionHistory(role, options) {
5942
- return this.transactionApi.getUserTransactionHistory({ role, ...options });
6282
+ async getUserTransactionHistory(options) {
6283
+ return this.transactionApi.getUserTransactionHistory(options);
5943
6284
  }
5944
6285
  /**
5945
6286
  * AUTH: Prepare existing transaction for client-side signing
@@ -5971,20 +6312,11 @@ class TransactionService {
5971
6312
  /* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
5972
6313
  return this.transactionApi.createAdminTransaction(request);
5973
6314
  } */
5974
- /**
5975
- * ADMIN: Get all tenant transactions with pagination
5976
- *
5977
- * @param options - Pagination options
5978
- * @returns Paginated tenant transactions
5979
- */
5980
- async getTenantTransactions(options) {
5981
- return this.transactionApi.getTenantTransactions(options || {});
5982
- }
5983
6315
  /**
5984
6316
  * ADMIN: Get paginated transactions with filtering and sorting
5985
6317
  */
5986
- async getPaginatedTransactions(params) {
5987
- return this.transactionApi.getPaginatedTransactions(params);
6318
+ async getPaginatedTransactions(options) {
6319
+ return this.transactionApi.getPaginatedTransactions(options);
5988
6320
  }
5989
6321
  /**
5990
6322
  * ADMIN: Export transactions to CSV
@@ -6739,7 +7071,191 @@ class AnalyticsApi {
6739
7071
  * ADMIN: Get transaction analytics with filtering and aggregation
6740
7072
  */
6741
7073
  async getTransactionAnalytics(request) {
6742
- return this.apiClient.post('/transactions/analytics', request);
7074
+ return this.apiClient.post('/analytics/transactions', request);
7075
+ }
7076
+ /**
7077
+ * ADMIN: Get campaign claim analytics with aggregation (charts, metrics, grouping)
7078
+ *
7079
+ * This endpoint is for aggregated analytics only (groupBy/metrics).
7080
+ * For enriched list data with nested objects, use campaign.getClaims() instead.
7081
+ *
7082
+ * @example Claims per campaign
7083
+ * ```typescript
7084
+ * const response = await analyticsApi.getCampaignClaimAnalytics({
7085
+ * filters: { status: 'COMPLETED' },
7086
+ * groupBy: ['campaignId'],
7087
+ * metrics: ['count'],
7088
+ * sortBy: 'count',
7089
+ * sortOrder: SortOrder.DESC,
7090
+ * limit: 10
7091
+ * });
7092
+ * ```
7093
+ *
7094
+ * @example Claims over time by status
7095
+ * ```typescript
7096
+ * const response = await analyticsApi.getCampaignClaimAnalytics({
7097
+ * groupBy: ['month', 'status'],
7098
+ * metrics: ['count'],
7099
+ * sortBy: 'month',
7100
+ * sortOrder: SortOrder.DESC,
7101
+ * startDate: new Date('2026-01-01'),
7102
+ * endDate: new Date('2026-12-31')
7103
+ * });
7104
+ * ```
7105
+ */
7106
+ async getCampaignClaimAnalytics(request) {
7107
+ return this.apiClient.post('/analytics/campaign-claims', request);
7108
+ }
7109
+ /**
7110
+ * ADMIN: Get user analytics with engagement metrics
7111
+ *
7112
+ * Returns aggregated user statistics including engagement rate, active users,
7113
+ * transaction metrics, and per-active-user averages (more accurate than all-user averages).
7114
+ *
7115
+ * Request structure matches TransactionAnalytics and CampaignClaimAnalytics for consistency:
7116
+ * - filters object for business-specific scoping
7117
+ * - startDate/endDate at root level for date range filtering
7118
+ *
7119
+ * @param request - Analytics request with optional filters and date range
7120
+ * @returns Aggregated user metrics with per-user and per-active-user averages
7121
+ *
7122
+ * @example Basic user analytics (all time, all businesses)
7123
+ * ```typescript
7124
+ * const analytics = await analyticsApi.getUserAnalytics({});
7125
+ * console.log(`Total users: ${analytics.totalUsers}`);
7126
+ * console.log(`Active users: ${analytics.activeUsers}`);
7127
+ * console.log(`Engagement rate: ${analytics.engagementRate}%`);
7128
+ *
7129
+ * // Per-user averages (includes inactive users)
7130
+ * console.log(`Avg transactions per user: ${analytics.averageTransactionsPerUser}`);
7131
+ *
7132
+ * // Per-active-user averages (only engaged users - more useful!)
7133
+ * console.log(`Avg transactions per active user: ${analytics.averageTransactionsPerActiveUser}`);
7134
+ * ```
7135
+ *
7136
+ * @example Filtered by date range
7137
+ * ```typescript
7138
+ * const analytics = await analyticsApi.getUserAnalytics({
7139
+ * startDate: new Date('2026-01-01'),
7140
+ * endDate: new Date('2026-01-31')
7141
+ * });
7142
+ * console.log(`January metrics:`);
7143
+ * console.log(`Active users: ${analytics.activeUsers}`);
7144
+ * console.log(`New users: ${analytics.newUsers}`);
7145
+ * console.log(`Date range applied: ${analytics.metadata.dateRange?.startDate} - ${analytics.metadata.dateRange?.endDate}`);
7146
+ * ```
7147
+ *
7148
+ * @example Business-specific analytics with filters
7149
+ * ```typescript
7150
+ * const analytics = await analyticsApi.getUserAnalytics({
7151
+ * filters: { businessId: 'biz-123' },
7152
+ * startDate: new Date('2026-01-01'),
7153
+ * endDate: new Date('2026-12-31')
7154
+ * });
7155
+ * console.log(`Business customer engagement in 2026:`);
7156
+ * console.log(`Active customers: ${analytics.activeUsers}`);
7157
+ * console.log(`Avg claims per active customer: ${analytics.averageClaimsPerActiveUser.toFixed(2)}`);
7158
+ * console.log(`Customer engagement rate: ${analytics.engagementRate.toFixed(1)}%`);
7159
+ * ```
7160
+ */
7161
+ async getUserAnalytics(request = {}) {
7162
+ return this.apiClient.post('/analytics/users', request);
7163
+ }
7164
+ /**
7165
+ * ADMIN: Get user transaction ranking with enriched user data
7166
+ *
7167
+ * Returns ranked list of users with full user details and transaction metrics.
7168
+ * Data enrichment happens via efficient SQL JOINs + UNION (handles legacy transactions).
7169
+ *
7170
+ * Use Cases:
7171
+ * - Admin leaderboards showing top users by activity
7172
+ * - User engagement analysis with full user context
7173
+ * - Identifying power users for campaigns
7174
+ *
7175
+ * @param request - Ranking request with filters, sorting, and limit
7176
+ * @returns Ranked list with user details (email, externalUserId) and transaction metrics
7177
+ *
7178
+ * @example Top 50 users by transaction count
7179
+ * ```typescript
7180
+ * const ranking = await analyticsApi.getUserRanking({
7181
+ * sortBy: 'totalTransactions',
7182
+ * sortOrder: SortOrder.DESC,
7183
+ * limit: 50
7184
+ * });
7185
+ *
7186
+ * ranking.results.forEach((user, index) => {
7187
+ * console.log(`#${index + 1}: ${user.email || user.externalUserId} - ${user.totalTransactions} transactions`);
7188
+ * });
7189
+ * ```
7190
+ *
7191
+ * @example Top users by STAMP spending
7192
+ * ```typescript
7193
+ * const ranking = await analyticsApi.getUserRanking({
7194
+ * filters: { tokenType: 'STAMP' },
7195
+ * sortBy: 'tokenSpent',
7196
+ * sortOrder: SortOrder.DESC,
7197
+ * limit: 20
7198
+ * });
7199
+ * ```
7200
+ *
7201
+ * @example Business-specific leaderboard with date range
7202
+ * ```typescript
7203
+ * const ranking = await analyticsApi.getUserRanking({
7204
+ * filters: {
7205
+ * businessId: 'business-uuid-here',
7206
+ * tokenType: 'CREDIT'
7207
+ * },
7208
+ * sortBy: 'totalTransactions',
7209
+ * sortOrder: SortOrder.DESC,
7210
+ * limit: 100,
7211
+ * startDate: new Date('2026-01-01'),
7212
+ * endDate: new Date('2026-12-31')
7213
+ * });
7214
+ * console.log(`Top ${ranking.totalUsers} users for business in 2026`);
7215
+ * ```
7216
+ */
7217
+ async getUserRanking(request = {}) {
7218
+ return this.apiClient.post('/analytics/users/ranking', request);
7219
+ }
7220
+ /**
7221
+ * ADMIN: Get business transaction ranking with enriched business data
7222
+ *
7223
+ * Returns ranked list of businesses with full business details and transaction metrics.
7224
+ * Data enrichment happens via efficient SQL JOINs + UNION (handles legacy transactions).
7225
+ *
7226
+ * @param request - Ranking request with filters, sorting, and limit
7227
+ * @returns Ranked list with business details and transaction metrics
7228
+ *
7229
+ * @example Top 50 businesses by transaction count
7230
+ * ```typescript
7231
+ * const ranking = await analyticsApi.getBusinessRanking({
7232
+ * sortBy: 'totalTransactions',
7233
+ * sortOrder: SortOrder.DESC,
7234
+ * limit: 50
7235
+ * });
7236
+ * ```
7237
+ */
7238
+ async getBusinessRanking(request = {}) {
7239
+ return this.apiClient.post('/analytics/businesses/ranking', request);
7240
+ }
7241
+ /**
7242
+ * ADMIN: Get monthly user retention analytics
7243
+ *
7244
+ * Returns monthly retention data with active/new/returning users and retention rates.
7245
+ * Replaces 13 separate API calls with 1 efficient recursive CTE query.
7246
+ *
7247
+ * @param request - Retention analytics request with monthsBack and filters
7248
+ * @returns Monthly retention data with user metrics and retention rates
7249
+ *
7250
+ * @example Last 13 months retention
7251
+ * ```typescript
7252
+ * const retention = await analyticsApi.getRetentionAnalytics({
7253
+ * monthsBack: 13
7254
+ * });
7255
+ * ```
7256
+ */
7257
+ async getRetentionAnalytics(request = {}) {
7258
+ return this.apiClient.post('/analytics/users/retention', request);
6743
7259
  }
6744
7260
  }
6745
7261
 
@@ -6764,6 +7280,47 @@ class AnalyticsService {
6764
7280
  async getTransactionAnalytics(request) {
6765
7281
  return this.analyticsApi.getTransactionAnalytics(request);
6766
7282
  }
7283
+ /**
7284
+ * ADMIN: Get campaign claim analytics with aggregation
7285
+ */
7286
+ async getCampaignClaimAnalytics(request) {
7287
+ return this.analyticsApi.getCampaignClaimAnalytics(request);
7288
+ }
7289
+ /**
7290
+ * ADMIN: Get user analytics with engagement metrics
7291
+ *
7292
+ * Returns aggregated user statistics including engagement rate, active users,
7293
+ * transaction metrics, and claims/redemption averages.
7294
+ */
7295
+ async getUserAnalytics(request = {}) {
7296
+ return this.analyticsApi.getUserAnalytics(request);
7297
+ }
7298
+ /**
7299
+ * ADMIN: Get user transaction ranking with enriched user data
7300
+ *
7301
+ * Returns ranked list of users with full user details (email, externalUserId)
7302
+ * and transaction metrics. Efficient data enrichment via SQL JOINs + UNION.
7303
+ */
7304
+ async getUserRanking(request = {}) {
7305
+ return this.analyticsApi.getUserRanking(request);
7306
+ }
7307
+ /**
7308
+ * ADMIN: Get business transaction ranking with enriched business data
7309
+ *
7310
+ * Returns ranked list of businesses with full business details and transaction metrics.
7311
+ */
7312
+ async getBusinessRanking(request = {}) {
7313
+ return this.analyticsApi.getBusinessRanking(request);
7314
+ }
7315
+ /**
7316
+ * ADMIN: Get monthly user retention analytics
7317
+ *
7318
+ * Returns monthly retention data with user metrics and retention rates.
7319
+ * Replaces 13 separate API calls with 1 efficient query.
7320
+ */
7321
+ async getRetentionAnalytics(request = {}) {
7322
+ return this.analyticsApi.getRetentionAnalytics(request);
7323
+ }
6767
7324
  }
6768
7325
 
6769
7326
  /**
@@ -6813,6 +7370,144 @@ class DonationService {
6813
7370
  }
6814
7371
  }
6815
7372
 
7373
+ /**
7374
+ * Platform-Agnostic TriggerSource API Client
7375
+ *
7376
+ * Handles all trigger source operations:
7377
+ * - CRUD operations for trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.)
7378
+ * - Trigger sources are independent entities that can be assigned to campaigns
7379
+ *
7380
+ * Uses @explorins/pers-shared DTOs for consistency with backend.
7381
+ */
7382
+ class TriggerSourceApi {
7383
+ constructor(apiClient) {
7384
+ this.apiClient = apiClient;
7385
+ }
7386
+ /**
7387
+ * PUBLIC: Get trigger sources with optional filters and pagination
7388
+ *
7389
+ * @param options - Filter and pagination options
7390
+ * @returns Paginated list of trigger sources
7391
+ *
7392
+ * @example
7393
+ * ```typescript
7394
+ * // Get all QR code trigger sources
7395
+ * const qrSources = await triggerSourceApi.getTriggerSources({ type: 'QR_CODE' });
7396
+ *
7397
+ * // Get trigger sources for a specific campaign
7398
+ * const campaignSources = await triggerSourceApi.getTriggerSources({
7399
+ * campaignId: 'campaign-123',
7400
+ * page: 1,
7401
+ * limit: 20
7402
+ * });
7403
+ * ```
7404
+ */
7405
+ async getTriggerSources(options) {
7406
+ const params = buildPaginationParams(options);
7407
+ if (options?.type)
7408
+ params.set('type', options.type);
7409
+ if (options?.businessId)
7410
+ params.set('businessId', options.businessId);
7411
+ if (options?.campaignId)
7412
+ params.set('campaignId', options.campaignId);
7413
+ if (options?.active !== undefined)
7414
+ params.set('active', String(options.active));
7415
+ const response = await this.apiClient.get(`/trigger-sources?${params.toString()}`);
7416
+ return normalizeToPaginated(response);
7417
+ }
7418
+ /**
7419
+ * PUBLIC: Get trigger source by ID
7420
+ *
7421
+ * @param triggerSourceId - UUID of the trigger source
7422
+ * @returns Trigger source details
7423
+ */
7424
+ async getTriggerSourceById(triggerSourceId) {
7425
+ return this.apiClient.get(`/trigger-sources/${triggerSourceId}`);
7426
+ }
7427
+ /**
7428
+ * ADMIN: Create a new trigger source
7429
+ *
7430
+ * @param triggerSource - Trigger source creation data
7431
+ * @returns Created trigger source
7432
+ *
7433
+ * @example
7434
+ * ```typescript
7435
+ * const qrSource = await triggerSourceApi.createTriggerSource({
7436
+ * type: 'QR_CODE',
7437
+ * name: 'Store Entrance QR',
7438
+ * description: 'QR code at main entrance',
7439
+ * businessId: 'business-123',
7440
+ * coordsLatitude: 47.6062,
7441
+ * coordsLongitude: -122.3321
7442
+ * });
7443
+ * ```
7444
+ */
7445
+ async createTriggerSource(triggerSource) {
7446
+ return this.apiClient.post('/trigger-sources', triggerSource);
7447
+ }
7448
+ /**
7449
+ * ADMIN: Update a trigger source
7450
+ *
7451
+ * @param triggerSourceId - UUID of the trigger source to update
7452
+ * @param triggerSource - Updated trigger source data (partial)
7453
+ * @returns Updated trigger source
7454
+ */
7455
+ async updateTriggerSource(triggerSourceId, triggerSource) {
7456
+ return this.apiClient.put(`/trigger-sources/${triggerSourceId}`, triggerSource);
7457
+ }
7458
+ /**
7459
+ * ADMIN: Delete (soft delete) a trigger source
7460
+ *
7461
+ * @param triggerSourceId - UUID of the trigger source to delete
7462
+ * @returns Success status
7463
+ */
7464
+ async deleteTriggerSource(triggerSourceId) {
7465
+ return this.apiClient.delete(`/trigger-sources/${triggerSourceId}`);
7466
+ }
7467
+ }
7468
+
7469
+ /**
7470
+ * Platform-Agnostic TriggerSource Service
7471
+ *
7472
+ * Contains trigger source business logic and operations that work across platforms.
7473
+ * No framework dependencies - pure TypeScript business logic.
7474
+ */
7475
+ class TriggerSourceService {
7476
+ constructor(triggerSourceApi) {
7477
+ this.triggerSourceApi = triggerSourceApi;
7478
+ }
7479
+ /**
7480
+ * Get trigger sources with optional filters
7481
+ */
7482
+ async getTriggerSources(options) {
7483
+ return this.triggerSourceApi.getTriggerSources(options);
7484
+ }
7485
+ /**
7486
+ * Get trigger source by ID
7487
+ */
7488
+ async getTriggerSourceById(triggerSourceId) {
7489
+ return this.triggerSourceApi.getTriggerSourceById(triggerSourceId);
7490
+ }
7491
+ /**
7492
+ * ADMIN: Create a new trigger source
7493
+ */
7494
+ async createTriggerSource(triggerSource) {
7495
+ return this.triggerSourceApi.createTriggerSource(triggerSource);
7496
+ }
7497
+ /**
7498
+ * ADMIN: Update a trigger source
7499
+ */
7500
+ async updateTriggerSource(triggerSourceId, triggerSource) {
7501
+ return this.triggerSourceApi.updateTriggerSource(triggerSourceId, triggerSource);
7502
+ }
7503
+ /**
7504
+ * ADMIN: Delete a trigger source
7505
+ */
7506
+ async deleteTriggerSource(triggerSourceId) {
7507
+ return this.triggerSourceApi.deleteTriggerSource(triggerSourceId);
7508
+ }
7509
+ }
7510
+
6816
7511
  /**
6817
7512
  * PERS SDK Configuration interfaces and utilities
6818
7513
  *
@@ -6828,8 +7523,7 @@ const DEFAULT_PERS_CONFIG = {
6828
7523
  timeout: 30000,
6829
7524
  retries: 3,
6830
7525
  tokenRefreshMargin: 60, // Refresh tokens 60 seconds before expiry
6831
- backgroundRefreshThreshold: 30, // Use background refresh if >30s remaining
6832
- autoRestoreSession: true // Automatically restore session on initialization
7526
+ backgroundRefreshThreshold: 30 // Use background refresh if >30s remaining
6833
7527
  };
6834
7528
  /**
6835
7529
  * Internal function to construct API root from environment
@@ -6897,10 +7591,7 @@ class AuthApi {
6897
7591
  * Authenticates a user in a business context with role included in JWT.
6898
7592
  *
6899
7593
  * @param jwt - Authentication token (passkey or Firebase JWT)
6900
- * @param options - Business authentication options
6901
- * @param options.businessId - The business ID to authenticate as
6902
- * - If user has single business membership, auto-selected
6903
- * - If user has multiple memberships without businessId, throws MULTIPLE_CONTEXT_SELECTION_REQUIRED
7594
+ * @param options - Business authentication options (businessId for multi-business users, auto-selected if single membership)
6904
7595
  * @returns Session response with business context and role in JWT
6905
7596
  * @throws MultipleContextSelectionError when businessId is required but not provided
6906
7597
  *
@@ -7019,10 +7710,7 @@ class AuthService {
7019
7710
  * the user's role within that business.
7020
7711
  *
7021
7712
  * @param jwt - Authentication token (passkey or Firebase JWT)
7022
- * @param options - Business authentication options
7023
- * @param options.businessId - The business ID to authenticate as
7024
- * - If user has single business membership, auto-selected
7025
- * - If user has multiple memberships without businessId, throws MULTIPLE_CONTEXT_SELECTION_REQUIRED
7713
+ * @param options - Business authentication options (businessId for multi-business users)
7026
7714
  * @returns Session response with business context and role baked into JWT
7027
7715
  * @throws MultipleContextSelectionRequiredError when businessId is required but not provided
7028
7716
  *
@@ -7139,8 +7827,12 @@ class AuthService {
7139
7827
  await extendedProvider.setProviderToken(providerToken);
7140
7828
  }
7141
7829
  if (authType && extendedProvider.setAuthType) {
7830
+ console.log('[AuthService] Storing auth type:', authType);
7142
7831
  await extendedProvider.setAuthType(authType);
7143
7832
  }
7833
+ else {
7834
+ console.warn('[AuthService] Auth type not stored - authType:', authType, 'has setAuthType:', !!extendedProvider.setAuthType);
7835
+ }
7144
7836
  // Emit authenticated status on successful token storage
7145
7837
  await this.emitAuthStatus(AuthStatus.AUTHENTICATED);
7146
7838
  }
@@ -7159,8 +7851,15 @@ class TokenRefreshManager {
7159
7851
  this.authService = authService;
7160
7852
  this.authProvider = authProvider;
7161
7853
  this.tokenRefreshMarginSeconds = 120; // 2 minutes
7854
+ this.lastValidationTime = 0;
7855
+ this.validationCacheDurationMs = 30000; // 30 seconds
7162
7856
  }
7163
7857
  async ensureValidToken() {
7858
+ const now = Date.now();
7859
+ // Skip validation if we checked recently (within cache duration)
7860
+ if (now - this.lastValidationTime < this.validationCacheDurationMs) {
7861
+ return;
7862
+ }
7164
7863
  try {
7165
7864
  const token = await this.authProvider.getToken();
7166
7865
  if (!token) {
@@ -7171,6 +7870,14 @@ class TokenRefreshManager {
7171
7870
  if (!refreshSuccess) {
7172
7871
  await this.authService.handleAuthFailure();
7173
7872
  }
7873
+ else {
7874
+ // Update validation time on successful refresh
7875
+ this.lastValidationTime = now;
7876
+ }
7877
+ }
7878
+ else {
7879
+ // Token is valid, update validation time
7880
+ this.lastValidationTime = now;
7174
7881
  }
7175
7882
  }
7176
7883
  catch (error) {
@@ -7254,42 +7961,77 @@ class MemoryTokenStorage {
7254
7961
  }
7255
7962
  }
7256
7963
  /**
7257
- * Token manager for authentication tokens
7964
+ * Token manager for authentication tokens with memory caching
7258
7965
  */
7259
7966
  class AuthTokenManager {
7260
7967
  constructor(storage = new LocalStorageTokenStorage()) {
7261
7968
  this.storage = storage;
7969
+ this.cache = {};
7262
7970
  }
7263
7971
  async getAccessToken() {
7972
+ // Return cached value if available
7973
+ if (this.cache.accessToken !== undefined) {
7974
+ return this.cache.accessToken;
7975
+ }
7264
7976
  const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
7265
7977
  // Ensure we return string for tokens
7266
- return typeof val === 'string' ? val : null;
7978
+ const token = typeof val === 'string' ? val : null;
7979
+ this.cache.accessToken = token;
7980
+ return token;
7267
7981
  }
7268
7982
  async setAccessToken(token) {
7983
+ this.cache.accessToken = token;
7269
7984
  await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
7270
7985
  }
7271
7986
  async getRefreshToken() {
7987
+ // Return cached value if available
7988
+ if (this.cache.refreshToken !== undefined) {
7989
+ return this.cache.refreshToken;
7990
+ }
7272
7991
  const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
7273
- return typeof val === 'string' ? val : null;
7992
+ const token = typeof val === 'string' ? val : null;
7993
+ this.cache.refreshToken = token;
7994
+ return token;
7274
7995
  }
7275
7996
  async setRefreshToken(token) {
7997
+ this.cache.refreshToken = token;
7276
7998
  await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
7277
7999
  }
7278
8000
  async getProviderToken() {
8001
+ // Return cached value if available
8002
+ if (this.cache.providerToken !== undefined) {
8003
+ return this.cache.providerToken;
8004
+ }
7279
8005
  const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
7280
- return typeof val === 'string' ? val : null;
8006
+ const token = typeof val === 'string' ? val : null;
8007
+ this.cache.providerToken = token;
8008
+ return token;
7281
8009
  }
7282
8010
  async setProviderToken(token) {
8011
+ this.cache.providerToken = token;
7283
8012
  await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
7284
8013
  }
7285
8014
  async getAuthType() {
8015
+ // Return cached value if available
8016
+ if (this.cache.authType !== undefined) {
8017
+ return this.cache.authType;
8018
+ }
7286
8019
  const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
7287
- return typeof authType === 'string' ? authType : null;
8020
+ const type = typeof authType === 'string' ? authType : null;
8021
+ this.cache.authType = type;
8022
+ return type;
7288
8023
  }
7289
8024
  async setAuthType(authType) {
8025
+ this.cache.authType = authType;
7290
8026
  await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
7291
8027
  }
7292
8028
  async setTokens(accessToken, refreshToken, providerToken) {
8029
+ // Update cache for all tokens
8030
+ this.cache.accessToken = accessToken;
8031
+ if (refreshToken)
8032
+ this.cache.refreshToken = refreshToken;
8033
+ if (providerToken)
8034
+ this.cache.providerToken = providerToken;
7293
8035
  await this.setAccessToken(accessToken);
7294
8036
  if (refreshToken)
7295
8037
  await this.setRefreshToken(refreshToken);
@@ -7297,13 +8039,23 @@ class AuthTokenManager {
7297
8039
  await this.setProviderToken(providerToken);
7298
8040
  }
7299
8041
  async clearAllTokens() {
8042
+ // Clear cache
8043
+ this.cache = {};
7300
8044
  await this.storage.clear();
7301
8045
  }
7302
8046
  async hasAccessToken() {
8047
+ // Use cached value if available to avoid storage read
8048
+ if (this.cache.accessToken !== undefined) {
8049
+ return !!this.cache.accessToken;
8050
+ }
7303
8051
  const token = await this.getAccessToken();
7304
8052
  return !!token;
7305
8053
  }
7306
8054
  async hasRefreshToken() {
8055
+ // Use cached value if available to avoid storage read
8056
+ if (this.cache.refreshToken !== undefined) {
8057
+ return !!this.cache.refreshToken;
8058
+ }
7307
8059
  const token = await this.getRefreshToken();
7308
8060
  return !!token;
7309
8061
  }
@@ -7531,6 +8283,7 @@ class DPoPManager {
7531
8283
  class DefaultAuthProvider {
7532
8284
  constructor(config = {}) {
7533
8285
  this.config = config;
8286
+ this.authTypeCache = null;
7534
8287
  this.authType = config.authType || exports.AccountOwnerType.USER;
7535
8288
  const storage = config.storage || this.createStorage();
7536
8289
  this.tokenManager = new AuthTokenManager(storage);
@@ -7591,6 +8344,15 @@ class DefaultAuthProvider {
7591
8344
  }
7592
8345
  async setAccessToken(token) {
7593
8346
  await this.tokenManager.setAccessToken(token);
8347
+ // Invalidate cache when token changes
8348
+ this.authTypeCache = null;
8349
+ // Eager DPoP key generation after login
8350
+ if (this.dpopManager) {
8351
+ // Fire and forget - generate keys in background
8352
+ this.dpopManager.ensureKeyPair().catch(err => {
8353
+ console.warn('[DefaultAuthProvider] DPoP key generation failed:', err);
8354
+ });
8355
+ }
7594
8356
  }
7595
8357
  async setRefreshToken(token) {
7596
8358
  await this.tokenManager.setRefreshToken(token);
@@ -7600,16 +8362,51 @@ class DefaultAuthProvider {
7600
8362
  }
7601
8363
  async clearTokens() {
7602
8364
  await this.tokenManager.clearAllTokens();
8365
+ // Invalidate cache on clear
8366
+ this.authTypeCache = null;
7603
8367
  if (this.dpopManager) {
7604
8368
  await this.dpopManager.clearKeys();
7605
8369
  }
7606
8370
  }
7607
8371
  async setTokens(accessToken, refreshToken, providerToken) {
7608
8372
  await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
8373
+ // Invalidate cache when tokens change
8374
+ this.authTypeCache = null;
7609
8375
  }
7610
8376
  async hasValidToken() {
7611
8377
  return this.tokenManager.hasAccessToken();
7612
8378
  }
8379
+ async getAuthType() {
8380
+ // Read from JWT instead of separate storage for single source of truth
8381
+ const token = await this.tokenManager.getAccessToken();
8382
+ if (!token) {
8383
+ this.authTypeCache = null;
8384
+ return null;
8385
+ }
8386
+ // Return cached result if token hasn't changed
8387
+ if (this.authTypeCache && this.authTypeCache.token === token) {
8388
+ return this.authTypeCache.type;
8389
+ }
8390
+ try {
8391
+ // Decode JWT payload (without verification - we just need to read the claim)
8392
+ const parts = token.split('.');
8393
+ if (parts.length !== 3) {
8394
+ console.warn('[DefaultAuthProvider] Invalid JWT format');
8395
+ return null;
8396
+ }
8397
+ const payloadJson = atob(parts[1]);
8398
+ const payload = JSON.parse(payloadJson);
8399
+ // Use accountOwnerType from AuthJWTPayload interface
8400
+ const authType = payload.accountType || null;
8401
+ // Cache result with token signature
8402
+ this.authTypeCache = { token, type: authType };
8403
+ return authType;
8404
+ }
8405
+ catch (error) {
8406
+ console.warn('[DefaultAuthProvider] Failed to parse auth type from JWT:', error);
8407
+ return null;
8408
+ }
8409
+ }
7613
8410
  }
7614
8411
 
7615
8412
  /**
@@ -7804,13 +8601,15 @@ class PersApiClient {
7804
8601
  * @internal
7805
8602
  */
7806
8603
  emitErrorEvent(errorDetails, errorMessage, endpoint, method, status, error) {
7807
- console.log('[PersApiClient] emitErrorEvent called', {
7808
- hasEvents: !!this._events,
7809
- instanceId: this._events?.instanceId ?? 'none',
7810
- subscriberCount: this._events?.subscriberCount ?? 0,
7811
- domain: errorDetails.domain,
7812
- message: errorDetails.message || errorMessage
7813
- });
8604
+ /* console.log('[PersApiClient] emitErrorEvent called', {
8605
+ hasEvents: !!this._events,
8606
+ instanceId: this._events?.instanceId ?? 'none',
8607
+ subscriberCount: this._events?.subscriberCount ?? 0,
8608
+ domain: errorDetails.domain,
8609
+ code: errorDetails.code,
8610
+ status: status,
8611
+ message: errorDetails.message || errorMessage
8612
+ }); */
7814
8613
  this._events?.emitError({
7815
8614
  domain: errorDetails.domain || 'external',
7816
8615
  type: errorDetails.code || 'API_ERROR',
@@ -8243,8 +9042,7 @@ class AuthManager {
8243
9042
  * throws `MULTIPLE_CONTEXT_SELECTION_REQUIRED` error with available options
8244
9043
  *
8245
9044
  * @param jwtToken - JWT token from auth provider (passkey, Firebase, etc.)
8246
- * @param options - Business authentication options
8247
- * @param options.businessId - The business ID to authenticate as (required for multi-business users)
9045
+ * @param options - Business authentication options (businessId for multi-business users)
8248
9046
  * @returns Promise resolving to authentication response with business context and role in JWT
8249
9047
  * @throws Error with code `MULTIPLE_CONTEXT_SELECTION_REQUIRED` when businessId is needed
8250
9048
  *
@@ -8671,26 +9469,49 @@ class UserManager {
8671
9469
  * requires administrator privileges and returns full user profiles including
8672
9470
  * private information.
8673
9471
  *
8674
- * @returns Promise resolving to array of all users with complete data
9472
+ * @param options - Pagination options (page, limit, sortBy, sortOrder)
9473
+ * @param search - Optional search query to filter users
9474
+ * @returns Promise resolving to paginated users with complete data
8675
9475
  * @throws {PersApiError} When not authenticated as admin
8676
9476
  *
8677
- * @example
9477
+ * @example Basic Usage
8678
9478
  * ```typescript
8679
9479
  * // Admin operation - requires admin authentication
8680
9480
  * try {
8681
- * const allUsers = await sdk.users.getAllUsers();
8682
- * console.log(`Total users: ${allUsers.length}`);
9481
+ * const result = await sdk.users.getAllUsers();
9482
+ * console.log(`Total users: ${result.pagination.total}`);
8683
9483
  *
8684
- * allUsers.forEach(user => {
8685
- * console.log(`${user.name} - ${user.email} - Active: ${user.isActive}`);
9484
+ * result.data.forEach(user => {
9485
+ * console.log(`${user.firstName} - ${user.email} - Active: ${user.isActive}`);
8686
9486
  * });
8687
9487
  * } catch (error) {
8688
9488
  * console.log('Admin access required');
8689
9489
  * }
8690
9490
  * ```
9491
+ *
9492
+ * @example With Sorting
9493
+ * ```typescript
9494
+ * import { SortOrder } from '@explorins/pers-sdk';
9495
+ *
9496
+ * // Get users sorted by email ascending
9497
+ * const result = await sdk.users.getAllUsers({
9498
+ * page: 1,
9499
+ * limit: 20,
9500
+ * sortBy: 'email',
9501
+ * sortOrder: SortOrder.ASC
9502
+ * });
9503
+ *
9504
+ * // Get users sorted by creation date (newest first)
9505
+ * const recent = await sdk.users.getAllUsers({
9506
+ * page: 1,
9507
+ * limit: 10,
9508
+ * sortBy: 'createdAt',
9509
+ * sortOrder: SortOrder.DESC
9510
+ * });
9511
+ * ```
8691
9512
  */
8692
- async getAllUsers(options) {
8693
- return this.userService.getAllRemoteUsers(options);
9513
+ async getAllUsers(options, search) {
9514
+ return this.userService.getAllRemoteUsers(options, search);
8694
9515
  }
8695
9516
  /**
8696
9517
  * Business/Admin: Create or update a user
@@ -9855,6 +10676,7 @@ class CampaignManager {
9855
10676
  * business partnerships, eligibility criteria, and claiming requirements.
9856
10677
  *
9857
10678
  * @param campaignId - Unique campaign identifier
10679
+ * @param include - Relations to include: 'triggerSources', 'businesses'
9858
10680
  * @returns Promise resolving to campaign data with complete details
9859
10681
  * @throws {PersApiError} When campaign with specified ID is not found
9860
10682
  *
@@ -9883,9 +10705,17 @@ class CampaignManager {
9883
10705
  * console.log('Campaign not found:', error.message);
9884
10706
  * }
9885
10707
  * ```
10708
+ *
10709
+ * @example With Include Relations
10710
+ * ```typescript
10711
+ * // Get campaign with trigger sources and businesses included
10712
+ * const campaign = await sdk.campaigns.getCampaignById('campaign-123', ['triggerSources', 'businesses']);
10713
+ * console.log('Trigger sources:', campaign.included?.triggerSources);
10714
+ * console.log('Businesses:', campaign.included?.businesses);
10715
+ * ```
9886
10716
  */
9887
- async getCampaignById(campaignId) {
9888
- return this.campaignService.getCampaignById(campaignId);
10717
+ async getCampaignById(campaignId, include) {
10718
+ return this.campaignService.getCampaignById(campaignId, include);
9889
10719
  }
9890
10720
  /**
9891
10721
  * Claim a campaign reward
@@ -9991,7 +10821,8 @@ class CampaignManager {
9991
10821
  * ```
9992
10822
  */
9993
10823
  async getUserClaims(options) {
9994
- return this.campaignService.getClaimsForLoggedUser(options);
10824
+ const { include, ...paginationOptions } = options || {};
10825
+ return this.campaignService.getClaimsForLoggedUser(paginationOptions, include);
9995
10826
  }
9996
10827
  /**
9997
10828
  * Get campaigns with pagination support
@@ -9999,13 +10830,7 @@ class CampaignManager {
9999
10830
  * Returns campaigns with pagination metadata for efficient data loading.
10000
10831
  * Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all.
10001
10832
  *
10002
- * @param options - Pagination and filter options
10003
- * @param options.active - Filter by active status (true/false/undefined for all)
10004
- * @param options.businessId - Filter by business engagement
10005
- * @param options.page - Page number (1-based, default: 1)
10006
- * @param options.limit - Items per page (default: 50)
10007
- * @param options.sortBy - Sort field ('name', 'createdAt', 'startDate')
10008
- * @param options.sortOrder - Sort direction ('ASC' or 'DESC')
10833
+ * @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, businessId, include)
10009
10834
  * @returns Promise resolving to paginated campaigns with metadata
10010
10835
  *
10011
10836
  * @example
@@ -10030,6 +10855,11 @@ class CampaignManager {
10030
10855
  * page: 1,
10031
10856
  * limit: 25
10032
10857
  * });
10858
+ *
10859
+ * // Include related data (trigger sources and businesses)
10860
+ * const campaignsWithRelations = await sdk.campaigns.getCampaigns({
10861
+ * include: ['triggerSources', 'businesses']
10862
+ * });
10033
10863
  * ```
10034
10864
  */
10035
10865
  async getCampaigns(options) {
@@ -10147,57 +10977,188 @@ class CampaignManager {
10147
10977
  async toggleCampaignTestnet(campaignId) {
10148
10978
  return this.campaignService.toggleCampaignTestnet(campaignId);
10149
10979
  }
10980
+ // ==========================================
10981
+ // CAMPAIGN TRIGGER OPERATIONS
10982
+ // Rules & limits for campaign activation
10983
+ // ==========================================
10150
10984
  /**
10151
- * Admin: Get campaign triggers
10985
+ * Admin: Get all campaign triggers (paginated)
10152
10986
  *
10153
- * Retrieves all available campaign trigger mechanisms that can be used to
10154
- * automatically activate campaigns based on user actions or system events.
10155
- * Requires administrator privileges.
10987
+ * Retrieves all campaign trigger rules that define rate limits, geo-validation,
10988
+ * conditions, and trigger types for campaign activation.
10156
10989
  *
10157
- * @returns Promise resolving to array of campaign trigger definitions
10990
+ * @param options - Pagination options
10991
+ * @returns Promise resolving to paginated list of campaign triggers
10158
10992
  *
10159
10993
  * @example
10160
10994
  * ```typescript
10161
- * // Admin operation - view available triggers
10162
- * const triggers = await sdk.campaigns.getCampaignTriggers();
10163
- *
10164
- * console.log('Available Campaign Triggers:');
10165
- * triggers.forEach(trigger => {
10166
- * console.log(`- ${trigger.name}: ${trigger.description}`);
10167
- * console.log(` Type: ${trigger.type}`);
10168
- * console.log(` Parameters: ${JSON.stringify(trigger.parameters)}`);
10995
+ * const triggers = await sdk.campaigns.getCampaignTriggers({ page: 1, limit: 20 });
10996
+ * console.log(`Found ${triggers.total} triggers`);
10997
+ * triggers.data.forEach(trigger => {
10998
+ * console.log(`- ${trigger.name}: max ${trigger.maxPerDayPerUser}/day`);
10169
10999
  * });
10170
11000
  * ```
10171
11001
  */
10172
11002
  async getCampaignTriggers(options) {
10173
- return this.campaignService.getCampaignTriggers();
11003
+ return this.campaignService.getCampaignTriggers(options);
11004
+ }
11005
+ /**
11006
+ * Admin: Get campaign trigger by ID
11007
+ *
11008
+ * @param triggerId - The campaign trigger ID
11009
+ * @returns Promise resolving to the campaign trigger
11010
+ *
11011
+ * @example
11012
+ * ```typescript
11013
+ * const trigger = await sdk.campaigns.getCampaignTriggerById('trigger-123');
11014
+ * console.log('Max per day:', trigger.maxPerDayPerUser);
11015
+ * ```
11016
+ */
11017
+ async getCampaignTriggerById(triggerId) {
11018
+ return this.campaignService.getCampaignTriggerById(triggerId);
10174
11019
  }
10175
11020
  /**
10176
- * Admin: Set campaign trigger
11021
+ * Admin: Create a new campaign trigger
11022
+ *
11023
+ * Creates a trigger rule that defines rate limits, geo-validation, and conditions
11024
+ * for campaign activation. Triggers are created independently and then assigned
11025
+ * to campaigns via `setCampaignTrigger()`.
10177
11026
  *
10178
- * Associates a specific trigger mechanism with a campaign, enabling automatic
10179
- * campaign activation based on defined conditions. Requires administrator privileges.
11027
+ * @param data - Trigger configuration
11028
+ * @returns Promise resolving to created trigger
11029
+ *
11030
+ * @example
11031
+ * ```typescript
11032
+ * const trigger = await sdk.campaigns.createCampaignTrigger({
11033
+ * name: 'Daily Check-in',
11034
+ * maxPerDayPerUser: 1,
11035
+ * maxPerUser: 100,
11036
+ * minCooldownSeconds: 3600,
11037
+ * maxGeoDistanceInMeters: 50,
11038
+ * triggerType: 'CLAIM_BY_USER'
11039
+ * });
11040
+ *
11041
+ * // Then assign to campaign
11042
+ * await sdk.campaigns.setCampaignTrigger(campaignId, trigger.id);
11043
+ * ```
11044
+ */
11045
+ async createCampaignTrigger(data) {
11046
+ const result = await this.campaignService.createCampaignTrigger(data);
11047
+ this.events?.emitSuccess({
11048
+ domain: 'campaign',
11049
+ type: 'CAMPAIGN_TRIGGER_CREATED',
11050
+ userMessage: `Trigger "${data.name}" created successfully`,
11051
+ details: { triggerId: result.id, name: data.name }
11052
+ });
11053
+ return result;
11054
+ }
11055
+ /**
11056
+ * Admin: Update an existing campaign trigger
11057
+ *
11058
+ * @param triggerId - The campaign trigger ID
11059
+ * @param data - Updated trigger configuration (partial)
11060
+ * @returns Promise resolving to updated trigger
11061
+ *
11062
+ * @example
11063
+ * ```typescript
11064
+ * const updated = await sdk.campaigns.updateCampaignTrigger('trigger-123', {
11065
+ * maxPerDayPerUser: 5,
11066
+ * minCooldownSeconds: 1800
11067
+ * });
11068
+ * ```
11069
+ */
11070
+ async updateCampaignTrigger(triggerId, data) {
11071
+ const result = await this.campaignService.updateCampaignTrigger(triggerId, data);
11072
+ this.events?.emitSuccess({
11073
+ domain: 'campaign',
11074
+ type: 'CAMPAIGN_TRIGGER_UPDATED',
11075
+ userMessage: 'Trigger updated successfully',
11076
+ details: { triggerId, updates: Object.keys(data) }
11077
+ });
11078
+ return result;
11079
+ }
11080
+ /**
11081
+ * Admin: Delete a campaign trigger
11082
+ *
11083
+ * @param triggerId - The campaign trigger ID
11084
+ * @returns Promise resolving to success status
11085
+ *
11086
+ * @example
11087
+ * ```typescript
11088
+ * await sdk.campaigns.deleteCampaignTrigger('trigger-123');
11089
+ * ```
11090
+ */
11091
+ async deleteCampaignTrigger(triggerId) {
11092
+ const result = await this.campaignService.deleteCampaignTrigger(triggerId);
11093
+ this.events?.emitSuccess({
11094
+ domain: 'campaign',
11095
+ type: 'CAMPAIGN_TRIGGER_DELETED',
11096
+ userMessage: 'Trigger deleted successfully',
11097
+ details: { triggerId }
11098
+ });
11099
+ return result;
11100
+ }
11101
+ /**
11102
+ * Admin: Assign a trigger to a campaign
11103
+ *
11104
+ * Associates a trigger rule with a campaign. A campaign can have only one trigger
11105
+ * at a time. Assigning a new trigger replaces any existing trigger.
10180
11106
  *
10181
11107
  * @param campaignId - ID of the campaign
10182
11108
  * @param triggerId - ID of the trigger to associate
10183
- * @returns Promise resolving to updated campaign data
10184
- * @throws {PersApiError} When not authenticated as admin or entities not found
11109
+ * @returns Promise resolving to updated campaign
10185
11110
  *
10186
11111
  * @example
10187
11112
  * ```typescript
10188
- * // Admin operation - set up automatic campaign trigger
10189
11113
  * const updated = await sdk.campaigns.setCampaignTrigger(
10190
- * 'new-user-welcome',
10191
- * 'user-registration-trigger'
11114
+ * 'campaign-123',
11115
+ * 'trigger-456'
10192
11116
  * );
10193
- *
10194
- * console.log('Trigger set for campaign:', updated.title);
10195
- * console.log('Trigger will activate on user registration');
11117
+ * console.log('Trigger assigned:', updated.trigger?.name);
10196
11118
  * ```
10197
11119
  */
10198
11120
  async setCampaignTrigger(campaignId, triggerId) {
10199
- return this.campaignService.setCampaignTrigger(campaignId, triggerId);
11121
+ const result = await this.campaignService.setCampaignTrigger(campaignId, triggerId);
11122
+ this.events?.emitSuccess({
11123
+ domain: 'campaign',
11124
+ type: 'CAMPAIGN_TRIGGER_ASSIGNED',
11125
+ userMessage: 'Trigger assigned to campaign',
11126
+ details: { campaignId, triggerId }
11127
+ });
11128
+ return result;
11129
+ }
11130
+ /**
11131
+ * Admin: Remove a trigger from a campaign
11132
+ *
11133
+ * Removes the trigger rule from a campaign. The trigger itself is not deleted
11134
+ * and can be reassigned to other campaigns.
11135
+ *
11136
+ * @param campaignId - ID of the campaign
11137
+ * @param triggerId - ID of the trigger to remove
11138
+ * @returns Promise resolving to updated campaign
11139
+ *
11140
+ * @example
11141
+ * ```typescript
11142
+ * const updated = await sdk.campaigns.removeCampaignTrigger(
11143
+ * 'campaign-123',
11144
+ * 'trigger-456'
11145
+ * );
11146
+ * console.log('Trigger removed:', updated.trigger === null);
11147
+ * ```
11148
+ */
11149
+ async removeCampaignTrigger(campaignId, triggerId) {
11150
+ const result = await this.campaignService.removeCampaignTrigger(campaignId, triggerId);
11151
+ this.events?.emitSuccess({
11152
+ domain: 'campaign',
11153
+ type: 'CAMPAIGN_TRIGGER_REMOVED',
11154
+ userMessage: 'Trigger removed from campaign',
11155
+ details: { campaignId, triggerId }
11156
+ });
11157
+ return result;
10200
11158
  }
11159
+ // ==========================================
11160
+ // TOKEN UNIT OPERATIONS
11161
+ // ==========================================
10201
11162
  /**
10202
11163
  * Admin: Create campaign token unit
10203
11164
  *
@@ -10406,8 +11367,8 @@ class CampaignManager {
10406
11367
  * });
10407
11368
  * ```
10408
11369
  */
10409
- async getCampaignClaims(filters) {
10410
- return this.campaignService.getCampaignClaims(filters);
11370
+ async getCampaignClaims(filters, include) {
11371
+ return this.campaignService.getCampaignClaims(filters, include);
10411
11372
  }
10412
11373
  /**
10413
11374
  * Admin: Get campaign claims by user ID
@@ -10442,8 +11403,8 @@ class CampaignManager {
10442
11403
  * console.log(`\nTotal rewards earned: ${totalRewards}`);
10443
11404
  * ```
10444
11405
  */
10445
- async getCampaignClaimsByUserId(userId, options) {
10446
- return this.campaignService.getCampaignClaimsByUserId(userId);
11406
+ async getCampaignClaimsByUserId(userId, options, include) {
11407
+ return this.campaignService.getCampaignClaimsByUserId(userId, options, include);
10447
11408
  }
10448
11409
  /**
10449
11410
  * Admin: Get campaign claims by business ID
@@ -10483,8 +11444,79 @@ class CampaignManager {
10483
11444
  * console.log(`\nTotal rewards distributed: ${totalRewardsDistributed}`);
10484
11445
  * ```
10485
11446
  */
10486
- async getCampaignClaimsByBusinessId(businessId, options) {
10487
- return this.campaignService.getCampaignClaimsByBusinessId(businessId);
11447
+ async getCampaignClaimsByBusinessId(businessId, options, include) {
11448
+ return this.campaignService.getCampaignClaimsByBusinessId(businessId, options, include);
11449
+ }
11450
+ // ==========================================
11451
+ // TRIGGER SOURCE ASSIGNMENT
11452
+ // Note: TriggerSource CRUD is in TriggerSourceManager (sdk.triggerSources)
11453
+ // ==========================================
11454
+ /**
11455
+ * Admin: Assign a trigger source to a campaign
11456
+ *
11457
+ * Associates a trigger source with a campaign, enabling the trigger source
11458
+ * to activate campaign rewards when triggered. A campaign can have multiple
11459
+ * trigger sources. Requires administrator privileges.
11460
+ *
11461
+ * Note: To create/update/delete trigger sources, use `sdk.triggerSources`.
11462
+ *
11463
+ * @param campaignId - Campaign UUID
11464
+ * @param triggerSourceId - Trigger source UUID
11465
+ * @returns Promise resolving to updated campaign with trigger source assigned
11466
+ * @throws {PersApiError} When not authenticated as admin or entities not found
11467
+ *
11468
+ * @example
11469
+ * ```typescript
11470
+ * // Create trigger source first
11471
+ * const source = await sdk.triggerSources.create({
11472
+ * type: 'QR_CODE',
11473
+ * name: 'Store Entrance QR'
11474
+ * });
11475
+ *
11476
+ * // Then assign to campaign
11477
+ * const updated = await sdk.campaigns.assignTriggerSource(
11478
+ * 'campaign-123',
11479
+ * source.id
11480
+ * );
11481
+ *
11482
+ * console.log('Trigger source assigned:', updated.triggerSourceIds.length);
11483
+ * ```
11484
+ */
11485
+ async assignTriggerSource(campaignId, triggerSourceId) {
11486
+ const result = await this.campaignService.assignTriggerSourceToCampaign(campaignId, triggerSourceId);
11487
+ this.events?.emitSuccess({
11488
+ domain: 'campaign',
11489
+ type: 'TRIGGER_SOURCE_ASSIGNED',
11490
+ userMessage: 'Trigger source assigned to campaign',
11491
+ details: { campaignId, triggerSourceId }
11492
+ });
11493
+ return result;
11494
+ }
11495
+ /**
11496
+ * Admin: Remove a trigger source from a campaign
11497
+ *
11498
+ * Removes the association between a trigger source and a campaign. The trigger
11499
+ * source itself is not deleted and can be reassigned to other campaigns.
11500
+ * Requires administrator privileges.
11501
+ *
11502
+ * @param campaignId - Campaign UUID
11503
+ * @param triggerSourceId - Trigger source UUID
11504
+ * @returns Promise resolving to updated campaign with trigger source removed
11505
+ * @throws {PersApiError} When not authenticated as admin or entities not found
11506
+ *
11507
+ * @example
11508
+ * ```typescript
11509
+ * // Remove a trigger source from a campaign
11510
+ * const updated = await sdk.campaigns.removeTriggerSource(
11511
+ * 'campaign-123',
11512
+ * 'source-456'
11513
+ * );
11514
+ *
11515
+ * console.log('Remaining trigger source IDs:', updated.triggerSourceIds.length);
11516
+ * ```
11517
+ */
11518
+ async removeTriggerSource(campaignId, triggerSourceId) {
11519
+ return this.campaignService.removeTriggerSourceFromCampaign(campaignId, triggerSourceId);
10488
11520
  }
10489
11521
  /**
10490
11522
  * Get the full campaign service for advanced operations
@@ -10868,8 +11900,57 @@ class RedemptionManager {
10868
11900
  * });
10869
11901
  * ```
10870
11902
  */
10871
- async getUserRedemptions(options) {
10872
- return this.redemptionService.getUserRedeems(options);
11903
+ async getUserRedemptions(options, include) {
11904
+ return this.redemptionService.getUserRedeems(options, include);
11905
+ }
11906
+ /**
11907
+ * Admin: Get all redemption redeems with filtering and enriched data
11908
+ *
11909
+ * Retrieves all redemption redeems across the platform with filtering capabilities.
11910
+ * This is an admin-level operation that allows monitoring and analytics of redemption
11911
+ * activity. Supports filtering by user, redemption offer, and enrichment of related entities.
11912
+ *
11913
+ * @param filters - Filter options (userId, redemptionId, pagination)
11914
+ * @param include - Optional relations to include for enrichment
11915
+ * @returns Promise resolving to paginated list of redemption redeems
11916
+ * @throws {PersApiError} When not authenticated as admin
11917
+ *
11918
+ * @example Get All Redeems
11919
+ * ```typescript
11920
+ * // Admin operation - get all redemption redeems
11921
+ * const { data: allRedeems, pagination } = await sdk.redemptions.getRedemptionRedeems();
11922
+ *
11923
+ * console.log(`Total redeems: ${pagination.total}`);
11924
+ * console.log(`Page ${pagination.page} of ${pagination.pages}`);
11925
+ * ```
11926
+ *
11927
+ * @example Filter by User
11928
+ * ```typescript
11929
+ * // Get redeems for specific user
11930
+ * const { data: userRedeems } = await sdk.redemptions.getRedemptionRedeems({
11931
+ * userId: 'user-123',
11932
+ * limit: 50
11933
+ * });
11934
+ *
11935
+ * console.log(`User has ${userRedeems.length} redemptions`);
11936
+ * ```
11937
+ *
11938
+ * @example With Enriched Data
11939
+ * ```typescript
11940
+ * const { data: redeems } = await sdk.redemptions.getRedemptionRedeems(
11941
+ * { page: 1, limit: 20 },
11942
+ * ['redemption', 'user', 'business']
11943
+ * );
11944
+ *
11945
+ * redeems.forEach(redeem => {
11946
+ * const redemptionName = redeem.included?.redemption?.name || 'Unknown';
11947
+ * const userEmail = redeem.included?.user?.email || redeem.userId;
11948
+ * console.log(`${redemptionName} - ${userEmail}`);
11949
+ * });
11950
+ * ```
11951
+ */
11952
+ async getRedemptionRedeems(filters, include) {
11953
+ return this.redemptionService.getRedemptionRedeems(filters, include);
10873
11954
  }
10874
11955
  /**
10875
11956
  * Admin: Create new redemption offer
@@ -11112,8 +12193,8 @@ class RedemptionManager {
11112
12193
  *
11113
12194
  * @example Administrative Reporting
11114
12195
  * ```typescript
11115
- * // Admin: Get all tenant transactions for analysis
11116
- * const allTransactions = await sdk.transactions.getTenantTransactions();
12196
+ * // Admin: Get paginated transactions for analysis
12197
+ * const allTransactions = await sdk.transactions.getPaginatedTransactions({ page: 1, limit: 100 });
11117
12198
  *
11118
12199
  * // Export transaction data
11119
12200
  * const csvBlob = await sdk.transactions.exportTransactionsCSV();
@@ -11136,13 +12217,18 @@ class TransactionManager {
11136
12217
  * Provides complete transaction audit trail and verification data.
11137
12218
  *
11138
12219
  * @param transactionId - Unique transaction identifier
12220
+ * @param include - Optional relations to include (sender, recipient, business) for enriched entity data
11139
12221
  * @returns Promise resolving to complete transaction data
11140
12222
  * @throws {PersApiError} When transaction with specified ID is not found or access denied
11141
12223
  *
11142
12224
  * @example
11143
12225
  * ```typescript
11144
12226
  * try {
11145
- * const transaction = await sdk.transactions.getTransactionById('txn-abc123');
12227
+ * // Get transaction with enriched sender/recipient data
12228
+ * const transaction = await sdk.transactions.getTransactionById(
12229
+ * 'txn-abc123',
12230
+ * ['sender', 'recipient', 'business']
12231
+ * );
11146
12232
  *
11147
12233
  * console.log('Transaction Details:');
11148
12234
  * console.log('ID:', transaction.id);
@@ -11155,16 +12241,19 @@ class TransactionManager {
11155
12241
  * console.log('Description:', transaction.description);
11156
12242
  * }
11157
12243
  *
11158
- * if (transaction.business) {
11159
- * console.log('Business:', transaction.business.displayName);
12244
+ * // Access enriched sender data
12245
+ * if (transaction.included?.sender) {
12246
+ * console.log('Sender:', transaction.included.sender);
11160
12247
  * }
11161
12248
  *
11162
- * // Show token movements
11163
- * if (transaction.tokenTransfers?.length) {
11164
- * console.log('\nToken Transfers:');
11165
- * transaction.tokenTransfers.forEach(transfer => {
11166
- * console.log(`${transfer.amount} ${transfer.token.symbol} ${transfer.direction}`);
11167
- * });
12249
+ * // Access enriched recipient data
12250
+ * if (transaction.included?.recipient) {
12251
+ * console.log('Recipient:', transaction.included.recipient);
12252
+ * }
12253
+ *
12254
+ * // Access enriched business data
12255
+ * if (transaction.included?.engagedBusiness) {
12256
+ * console.log('Business:', transaction.included.engagedBusiness.displayName);
11168
12257
  * }
11169
12258
  *
11170
12259
  * // Show blockchain confirmation
@@ -11177,8 +12266,8 @@ class TransactionManager {
11177
12266
  * }
11178
12267
  * ```
11179
12268
  */
11180
- async getTransactionById(transactionId) {
11181
- return this.transactionService.getTransactionById(transactionId);
12269
+ async getTransactionById(transactionId, include) {
12270
+ return this.transactionService.getTransactionById(transactionId, include);
11182
12271
  }
11183
12272
  /**
11184
12273
  * Create a new transaction
@@ -11272,150 +12361,62 @@ class TransactionManager {
11272
12361
  /**
11273
12362
  * Get user's transaction history
11274
12363
  *
11275
- * Retrieves transaction history for the authenticated user, filtered by
11276
- * transaction role. Provides chronological view of all user's loyalty
12364
+ * Retrieves transaction history for the authenticated user with comprehensive
12365
+ * filtering options. Provides chronological view of all user's loyalty
11277
12366
  * activities including purchases, rewards, redemptions, and transfers.
12367
+ * Optionally enrich with related entities (sender, recipient, business).
11278
12368
  *
11279
- * @param role - Optional role filter (TransactionRole.SENDER, TransactionRole.RECIPIENT). If undefined, returns all user transactions
11280
- * @param limit - Maximum number of transactions to return (default: 1000)
11281
- * @returns Promise resolving to array of user's transactions
12369
+ * @param options - Query options including filters, pagination, and include relations
12370
+ * @returns Promise resolving to paginated transaction data with optional included entities
11282
12371
  *
11283
12372
  * @example All Transactions
11284
12373
  * ```typescript
11285
- * const allTransactions = await sdk.transactions.getUserTransactionHistory('ALL');
12374
+ * const result = await sdk.transactions.getUserTransactionHistory();
11286
12375
  *
11287
- * console.log(` Transaction History (${allTransactions.length} transactions):`);
12376
+ * console.log(`Transaction History (${result.data.length} of ${result.pagination.total} transactions)`);
11288
12377
  *
11289
- * allTransactions.forEach((transaction, index) => {
12378
+ * result.data.forEach((transaction, index) => {
11290
12379
  * const date = new Date(transaction.createdAt).toLocaleDateString();
11291
12380
  * console.log(`\n${index + 1}. ${transaction.type} - ${date}`);
11292
12381
  * console.log(` ${transaction.description || 'No description'}`);
11293
12382
  * console.log(` Status: ${transaction.status}`);
11294
- *
11295
- * if (transaction.amount && transaction.currency) {
11296
- * console.log(` Amount: ${transaction.amount} ${transaction.currency}`);
11297
- * }
11298
- *
11299
- * if (transaction.business) {
11300
- * console.log(` Business: ${transaction.business.displayName}`);
11301
- * }
11302
12383
  * });
11303
12384
  * ```
11304
12385
  *
11305
- * @example Filtered by Type
12386
+ * @example Filter by Role and Status
11306
12387
  * ```typescript
11307
- * // Get only purchase transactions
11308
- * const purchases = await sdk.transactions.getUserTransactionHistory('PURCHASE');
11309
- *
11310
- * let totalSpent = 0;
11311
- * let totalRewards = 0;
11312
- *
11313
- * purchases.forEach(purchase => {
11314
- * if (purchase.amount) {
11315
- * totalSpent += purchase.amount;
11316
- * }
11317
- *
11318
- * if (purchase.tokensEarned?.length) {
11319
- * purchase.tokensEarned.forEach(reward => {
11320
- * totalRewards += reward.amount;
11321
- * });
11322
- * }
12388
+ * // Get only completed sent transactions
12389
+ * const sent = await sdk.transactions.getUserTransactionHistory({
12390
+ * role: TransactionRole.SENDER,
12391
+ * status: TransactionStatus.COMPLETED,
12392
+ * include: ['recipient', 'business'],
12393
+ * page: 1,
12394
+ * limit: 50
11323
12395
  * });
11324
12396
  *
11325
- * console.log('Purchase Summary:');
11326
- * console.log(`Total purchases: ${purchases.length}`);
11327
- * console.log(`Total spent: $${totalSpent.toFixed(2)}`);
11328
- * console.log(`Total rewards earned: ${totalRewards} points`);
11329
- * ```
11330
- *
11331
- * @example Recent Activity
11332
- * ```typescript
11333
- * const recentTransactions = await sdk.transactions.getUserTransactionHistory('ALL');
11334
- *
11335
- * // Filter to last 30 days
11336
- * const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
11337
- * const recentActivity = recentTransactions.filter(t =>
11338
- * new Date(t.createdAt) > thirtyDaysAgo
11339
- * );
11340
- *
11341
- * console.log(`Recent Activity (${recentActivity.length} transactions in last 30 days):`);
11342
- *
11343
- * // Group by type
11344
- * const activityByType = recentActivity.reduce((acc, t) => {
11345
- * acc[t.type] = (acc[t.type] || 0) + 1;
11346
- * return acc;
11347
- * }, {});
11348
- *
11349
- * Object.entries(activityByType).forEach(([type, count]) => {
11350
- * console.log(`${type}: ${count} transactions`);
12397
+ * sent.data.forEach(tx => {
12398
+ * console.log(`Sent to: ${tx.included?.recipient?.displayName || tx.recipientAddress}`);
11351
12399
  * });
11352
12400
  * ```
11353
- */
11354
- async getUserTransactionHistory(role, options) {
11355
- return this.transactionService.getUserTransactionHistory(role, options);
11356
- }
11357
- /**
11358
- * Admin: Get all tenant transactions
11359
- *
11360
- * Retrieves all transactions across the entire tenant/organization for
11361
- * comprehensive reporting and analysis. This operation requires administrator
11362
- * privileges and provides system-wide transaction visibility.
11363
- *
11364
- * @param limit - Maximum number of transactions to return (default: 1000)
11365
- * @returns Promise resolving to array of all tenant transactions
11366
- * @throws {PersApiError} When not authenticated as administrator
11367
12401
  *
11368
- * @example
12402
+ * @example Filter by Trigger Process (Campaign/Redemption)
11369
12403
  * ```typescript
11370
- * // Admin operation - comprehensive transaction analysis
11371
- * const allTransactions = await sdk.transactions.getTenantTransactions();
11372
- *
11373
- * console.log(`Tenant Transaction Overview:`);
11374
- * console.log(`Total transactions: ${allTransactions.length}`);
11375
- *
11376
- * // Analyze by status
11377
- * const statusCounts = allTransactions.reduce((acc, t) => {
11378
- * acc[t.status] = (acc[t.status] || 0) + 1;
11379
- * return acc;
11380
- * }, {});
11381
- *
11382
- * console.log('\nBy status:');
11383
- * Object.entries(statusCounts).forEach(([status, count]) => {
11384
- * console.log(`${status}: ${count} transactions`);
12404
+ * // Get all transactions triggered by a specific campaign claim
12405
+ * const claimTxs = await sdk.transactions.getUserTransactionHistory({
12406
+ * triggerProcessId: 'claim-abc123',
12407
+ * include: ['sender', 'recipient', 'business']
11385
12408
  * });
11386
12409
  *
11387
- * // Analyze by type
11388
- * const typeCounts = allTransactions.reduce((acc, t) => {
11389
- * acc[t.type] = (acc[t.type] || 0) + 1;
11390
- * return acc;
11391
- * }, {});
11392
- *
11393
- * console.log('\nBy type:');
11394
- * Object.entries(typeCounts).forEach(([type, count]) => {
11395
- * console.log(`${type}: ${count} transactions`);
12410
+ * claimTxs.data.forEach(tx => {
12411
+ * console.log('Transaction from claim:', tx.id);
12412
+ * if (tx.included?.engagedBusiness) {
12413
+ * console.log('Business:', tx.included.engagedBusiness.displayName);
12414
+ * }
11396
12415
  * });
11397
- *
11398
- * // Calculate volume metrics
11399
- * const totalVolume = allTransactions.reduce((sum, t) =>
11400
- * sum + (t.amount || 0), 0
11401
- * );
11402
- *
11403
- * const avgTransactionSize = totalVolume / allTransactions.length;
11404
- *
11405
- * console.log('\nVolume metrics:');
11406
- * console.log(`Total volume: $${totalVolume.toFixed(2)}`);
11407
- * console.log(`Average transaction: $${avgTransactionSize.toFixed(2)}`);
11408
- *
11409
- * // Recent activity analysis
11410
- * const last24Hours = allTransactions.filter(t =>
11411
- * new Date(t.createdAt) > new Date(Date.now() - 24 * 60 * 60 * 1000)
11412
- * );
11413
- *
11414
- * console.log(`\n⏰ Last 24 hours: ${last24Hours.length} transactions`);
11415
12416
  * ```
11416
12417
  */
11417
- async getTenantTransactions(options) {
11418
- return this.transactionService.getTenantTransactions(options);
12418
+ async getUserTransactionHistory(options) {
12419
+ return this.transactionService.getUserTransactionHistory(options);
11419
12420
  }
11420
12421
  /**
11421
12422
  * Admin: Get paginated transactions
@@ -11424,7 +12425,7 @@ class TransactionManager {
11424
12425
  * handling. This operation requires administrator privileges and is used for
11425
12426
  * detailed transaction analysis and reporting interfaces.
11426
12427
  *
11427
- * @param params - Pagination and filtering parameters
12428
+ * @param options - Pagination, filtering parameters, and optional include relations
11428
12429
  * @returns Promise resolving to paginated transaction results with metadata
11429
12430
  * @throws {PersApiError} When not authenticated as administrator
11430
12431
  *
@@ -11479,9 +12480,24 @@ class TransactionManager {
11479
12480
  * console.log(`$${transaction.amount} - ${transaction.business?.displayName}`);
11480
12481
  * });
11481
12482
  * ```
12483
+ *
12484
+ * @example With Included Relations
12485
+ * ```typescript
12486
+ * // Include sender and recipient entities
12487
+ * const result = await sdk.transactions.getPaginatedTransactions({
12488
+ * page: 1,
12489
+ * limit: 50,
12490
+ * include: ['sender', 'recipient', 'business']
12491
+ * });
12492
+ *
12493
+ * result.data.forEach(tx => {
12494
+ * if (tx.included?.sender) console.log('From:', tx.included.sender);
12495
+ * if (tx.included?.recipient) console.log('To:', tx.included.recipient);
12496
+ * });
12497
+ * ```
11482
12498
  */
11483
- async getPaginatedTransactions(params) {
11484
- return this.transactionService.getPaginatedTransactions(params);
12499
+ async getPaginatedTransactions(options) {
12500
+ return this.transactionService.getPaginatedTransactions(options);
11485
12501
  }
11486
12502
  /**
11487
12503
  * Admin: Export transactions as CSV
@@ -13144,6 +14160,179 @@ class AnalyticsManager {
13144
14160
  async getTransactionAnalytics(request) {
13145
14161
  return this.analyticsService.getTransactionAnalytics(request);
13146
14162
  }
14163
+ /**
14164
+ * Get campaign claim analytics with aggregation
14165
+ *
14166
+ * Retrieves aggregated analytics for campaign claims with flexible grouping,
14167
+ * filtering, and metric selection. Provides insights into campaign performance,
14168
+ * claim patterns, and user engagement across the loyalty ecosystem.
14169
+ *
14170
+ * @param request - Analytics request with filters, groupBy, and metrics
14171
+ * @returns Promise resolving to campaign claim analytics data
14172
+ *
14173
+ * @example Claims per campaign
14174
+ * ```typescript
14175
+ * const analytics = await sdk.analytics.getCampaignClaimAnalytics({
14176
+ * filters: { status: 'COMPLETED' },
14177
+ * groupBy: ['campaignId'],
14178
+ * metrics: ['count'],
14179
+ * sortBy: 'count',
14180
+ * sortOrder: 'DESC',
14181
+ * limit: 10
14182
+ * });
14183
+ *
14184
+ * console.log('Top campaigns:', analytics.results);
14185
+ * ```
14186
+ */
14187
+ async getCampaignClaimAnalytics(request) {
14188
+ return this.analyticsService.getCampaignClaimAnalytics(request);
14189
+ }
14190
+ /**
14191
+ * Get user analytics with engagement metrics
14192
+ *
14193
+ * Retrieves aggregated user statistics including engagement rate, active users,
14194
+ * transaction metrics, and per-active-user averages. Provides comprehensive
14195
+ * insights into user behavior, engagement patterns, and platform adoption metrics.
14196
+ *
14197
+ * NEW: Added per-active-user metrics (averageTransactionsPerActiveUser, etc.) which
14198
+ * show concentrated engagement among active users - more useful than diluted all-user averages.
14199
+ *
14200
+ * Request structure matches TransactionAnalytics and CampaignClaimAnalytics:
14201
+ * - Use filters object for business-specific scoping
14202
+ * - startDate/endDate at root level for date range filtering
14203
+ *
14204
+ * @param request - Analytics request with optional filters and date range
14205
+ * @returns Promise resolving to user analytics data with per-user and per-active-user metrics
14206
+ *
14207
+ * @example Basic user analytics (compare per-user vs per-active-user)
14208
+ * ```typescript
14209
+ * const analytics = await sdk.analytics.getUserAnalytics({});
14210
+ * console.log(`Total users: ${analytics.totalUsers}`);
14211
+ * console.log(`Active users: ${analytics.activeUsers} (${analytics.engagementRate.toFixed(1)}%)`);
14212
+ *
14213
+ * // Per-user averages (includes inactive users - lower numbers)
14214
+ * console.log(`\\nPer-User Averages (all users):`);
14215
+ * console.log(` Transactions: ${analytics.averageTransactionsPerUser.toFixed(2)}`);
14216
+ * console.log(` Claims: ${analytics.averageClaimsPerUser.toFixed(2)}`);
14217
+ * console.log(` Redemptions: ${analytics.averageRedemptionsPerUser.toFixed(2)}`);
14218
+ *
14219
+ * // Per-active-user averages (only engaged users - higher, more meaningful numbers)
14220
+ * console.log(`\\nPer-Active-User Averages (engaged users only):`);
14221
+ * console.log(` Transactions: ${analytics.averageTransactionsPerActiveUser.toFixed(2)}`);
14222
+ * console.log(` Claims: ${analytics.averageClaimsPerActiveUser.toFixed(2)}`);
14223
+ * console.log(` Redemptions: ${analytics.averageRedemptionsPerActiveUser.toFixed(2)}`);
14224
+ * ```
14225
+ *
14226
+ * @example Monthly user analytics with date range
14227
+ * ```typescript
14228
+ * const analytics = await sdk.analytics.getUserAnalytics({
14229
+ * startDate: new Date('2026-02-01'),
14230
+ * endDate: new Date('2026-02-28')
14231
+ * });
14232
+ *
14233
+ * console.log('February 2026 User Metrics:');
14234
+ * console.log(`Total users: ${analytics.totalUsers}`);
14235
+ * console.log(`Active users: ${analytics.activeUsers}`);
14236
+ * console.log(`New users: ${analytics.newUsers}`);
14237
+ * console.log(`Engagement rate: ${analytics.engagementRate.toFixed(2)}%`);
14238
+ * console.log(`Total transaction volume: $${analytics.totalTransactionVolume.toLocaleString()}`);
14239
+ * console.log(`Avg transaction amount: $${analytics.averageTransactionAmount.toFixed(2)}`);
14240
+ * console.log(`\\nFilters applied: ${analytics.metadata.dateRange?.startDate} to ${analytics.metadata.dateRange?.endDate}`);
14241
+ * ```
14242
+ *
14243
+ * @example Business-specific user analytics with filters
14244
+ * ```typescript
14245
+ * const analytics = await sdk.analytics.getUserAnalytics({
14246
+ * filters: { businessId: 'business-123' },
14247
+ * startDate: new Date('2026-01-01'),
14248
+ * endDate: new Date('2026-12-31')
14249
+ * });
14250
+ *
14251
+ * console.log(`Business customer metrics for 2026:`);
14252
+ * console.log(`Active customers: ${analytics.activeUsers} / ${analytics.totalUsers}`);
14253
+ * console.log(`Customer engagement: ${analytics.engagementRate.toFixed(1)}%`);
14254
+ * console.log(`Avg spend per customer: $${analytics.averageTransactionAmount.toFixed(2)}`);
14255
+ * console.log(`Avg spend per active customer: $${(analytics.totalTransactionVolume / analytics.activeUsers).toFixed(2)}`);
14256
+ * console.log(`Avg claims per active customer: ${analytics.averageClaimsPerActiveUser.toFixed(2)}`);
14257
+ * ```
14258
+ */
14259
+ async getUserAnalytics(request = {}) {
14260
+ return this.analyticsService.getUserAnalytics(request);
14261
+ }
14262
+ /**
14263
+ * Get user transaction ranking with enriched user data
14264
+ *
14265
+ * Retrieves ranked list of users with full user details (email, externalUserId)
14266
+ * and transaction metrics. Data enrichment happens via efficient SQL JOINs + UNION
14267
+ * (handles legacy transactions). Ideal for leaderboards, engagement analysis, and
14268
+ * identifying power users for targeted campaigns.
14269
+ *
14270
+ * Use Cases:
14271
+ * - Admin leaderboards showing top users by activity
14272
+ * - User engagement analysis with full user context
14273
+ * - Identifying power users for campaigns
14274
+ * - Customer segmentation by transaction behavior
14275
+ *
14276
+ * @param request - Ranking request with filters, sorting, and limit
14277
+ * @returns Promise resolving to ranked user list with transaction metrics
14278
+ *
14279
+ * @example Top 50 users by transaction count
14280
+ * ```typescript
14281
+ * const ranking = await sdk.analytics.getUserRanking({
14282
+ * sortBy: 'totalTransactions',
14283
+ * sortOrder: 'DESC',
14284
+ * limit: 50
14285
+ * });
14286
+ *
14287
+ * console.log(`Top ${ranking.totalUsers} users:`);
14288
+ * ranking.results.forEach((user, index) => {
14289
+ * console.log(`#${index + 1}: ${user.email || user.externalUserId}`);
14290
+ * console.log(` Transactions: ${user.totalTransactions}`);
14291
+ * console.log(` Token spent: ${user.tokenSpent}`);
14292
+ * });
14293
+ * ```
14294
+ *
14295
+ * @example Top users by STAMP spending
14296
+ * ```typescript
14297
+ * const ranking = await sdk.analytics.getUserRanking({
14298
+ * filters: { tokenType: 'STAMP' },
14299
+ * sortBy: 'tokenSpent',
14300
+ * sortOrder: 'DESC',
14301
+ * limit: 20
14302
+ * });
14303
+ *
14304
+ * console.log('Top 20 STAMP spenders:');
14305
+ * ranking.results.forEach((user, index) => {
14306
+ * const identifier = user.email || user.externalUserId || user.userId;
14307
+ * console.log(`${index + 1}. ${identifier} - ${user.tokenSpent} STAMP`);
14308
+ * });
14309
+ * ```
14310
+ */
14311
+ async getUserRanking(request = {}) {
14312
+ return this.analyticsService.getUserRanking(request);
14313
+ }
14314
+ /**
14315
+ * Get business transaction ranking with enriched business data
14316
+ *
14317
+ * Returns ranked list of businesses with full business details and transaction metrics.
14318
+ *
14319
+ * @param request - Ranking request with filters, sorting, and limit
14320
+ * @returns Promise resolving to ranked business list
14321
+ */
14322
+ async getBusinessRanking(request = {}) {
14323
+ return this.analyticsService.getBusinessRanking(request);
14324
+ }
14325
+ /**
14326
+ * Get monthly user retention analytics
14327
+ *
14328
+ * Returns monthly retention data with user metrics and retention rates.
14329
+ *
14330
+ * @param request - Retention request with monthsBack and filters
14331
+ * @returns Promise resolving to monthly retention data
14332
+ */
14333
+ async getRetentionAnalytics(request = {}) {
14334
+ return this.analyticsService.getRetentionAnalytics(request);
14335
+ }
13147
14336
  /**
13148
14337
  * Get the full analytics service for advanced operations
13149
14338
  *
@@ -13238,6 +14427,222 @@ class DonationManager {
13238
14427
  }
13239
14428
  }
13240
14429
 
14430
+ /**
14431
+ * TriggerSource Manager - Clean, high-level interface for trigger source operations
14432
+ *
14433
+ * Manages trigger sources which are physical or digital activation points for campaigns:
14434
+ * - QR_CODE: Scannable QR codes
14435
+ * - NFC_TAG: NFC tap points
14436
+ * - GPS_GEOFENCE: Location-based triggers
14437
+ * - API_WEBHOOK: External system integration
14438
+ * - TRANSACTION: Purchase/payment based triggers
14439
+ *
14440
+ * Trigger sources are independent entities that can be created, managed, and then
14441
+ * assigned to campaigns. This separation allows reuse and flexible campaign configuration.
14442
+ *
14443
+ * @group Managers
14444
+ * @category TriggerSource Management
14445
+ *
14446
+ * @example Basic TriggerSource Operations
14447
+ * ```typescript
14448
+ * // Get all QR code trigger sources
14449
+ * const qrSources = await sdk.triggerSources.getAll({ type: 'QR_CODE' });
14450
+ *
14451
+ * // Create a new QR code trigger source
14452
+ * const source = await sdk.triggerSources.create({
14453
+ * type: 'QR_CODE',
14454
+ * name: 'Store Entrance QR',
14455
+ * businessId: 'business-123'
14456
+ * });
14457
+ *
14458
+ * // Assign to campaign (via CampaignManager)
14459
+ * await sdk.campaigns.assignTriggerSource('campaign-456', source.id);
14460
+ * ```
14461
+ */
14462
+ class TriggerSourceManager {
14463
+ constructor(apiClient, events) {
14464
+ this.apiClient = apiClient;
14465
+ this.events = events;
14466
+ const triggerSourceApi = new TriggerSourceApi(apiClient);
14467
+ this.triggerSourceService = new TriggerSourceService(triggerSourceApi);
14468
+ }
14469
+ /**
14470
+ * Get trigger sources with optional filters and pagination
14471
+ *
14472
+ * Retrieves trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.)
14473
+ * that can be used to activate campaigns. Supports filtering by type, business,
14474
+ * campaign association, and active status.
14475
+ *
14476
+ * @param options - Filter and pagination options
14477
+ * @returns Promise resolving to paginated trigger sources
14478
+ *
14479
+ * @example
14480
+ * ```typescript
14481
+ * // Get all QR code trigger sources
14482
+ * const qrSources = await sdk.triggerSources.getAll({ type: 'QR_CODE' });
14483
+ *
14484
+ * // Get trigger sources for a specific business
14485
+ * const businessSources = await sdk.triggerSources.getAll({
14486
+ * businessId: 'business-123',
14487
+ * active: true,
14488
+ * page: 1,
14489
+ * limit: 20
14490
+ * });
14491
+ *
14492
+ * // Get trigger sources assigned to a campaign
14493
+ * const campaignSources = await sdk.triggerSources.getAll({
14494
+ * campaignId: 'campaign-456'
14495
+ * });
14496
+ * ```
14497
+ */
14498
+ async getAll(options) {
14499
+ return this.triggerSourceService.getTriggerSources(options);
14500
+ }
14501
+ /**
14502
+ * Get trigger source by ID
14503
+ *
14504
+ * Retrieves detailed information for a specific trigger source including
14505
+ * its type, location, metadata, and usage analytics.
14506
+ *
14507
+ * @param triggerSourceId - UUID of the trigger source
14508
+ * @returns Promise resolving to trigger source details
14509
+ * @throws {PersApiError} When trigger source with specified ID is not found
14510
+ *
14511
+ * @example
14512
+ * ```typescript
14513
+ * const source = await sdk.triggerSources.getById('source-123');
14514
+ *
14515
+ * console.log('Trigger Source:', source.name);
14516
+ * console.log('Type:', source.type);
14517
+ * console.log('Active:', source.isActive);
14518
+ *
14519
+ * if (source.coordsLatitude && source.coordsLongitude) {
14520
+ * console.log('Location:', source.coordsLatitude, source.coordsLongitude);
14521
+ * }
14522
+ * ```
14523
+ */
14524
+ async getById(triggerSourceId) {
14525
+ return this.triggerSourceService.getTriggerSourceById(triggerSourceId);
14526
+ }
14527
+ /**
14528
+ * Admin: Create a new trigger source
14529
+ *
14530
+ * Creates a new trigger source (QR code, NFC tag, GPS geofence, webhook, or
14531
+ * transaction-based trigger) that can be assigned to campaigns. Requires
14532
+ * administrator privileges.
14533
+ *
14534
+ * @param triggerSource - Trigger source creation data
14535
+ * @returns Promise resolving to created trigger source
14536
+ * @throws {PersApiError} When not authenticated as admin or validation fails
14537
+ *
14538
+ * @example
14539
+ * ```typescript
14540
+ * // Create a QR code trigger source
14541
+ * const qrSource = await sdk.triggerSources.create({
14542
+ * type: 'QR_CODE',
14543
+ * name: 'Store Entrance QR',
14544
+ * description: 'QR code at main entrance',
14545
+ * businessId: 'business-123',
14546
+ * coordsLatitude: 47.6062,
14547
+ * coordsLongitude: -122.3321
14548
+ * });
14549
+ *
14550
+ * // Create an NFC tag trigger source
14551
+ * const nfcSource = await sdk.triggerSources.create({
14552
+ * type: 'NFC_TAG',
14553
+ * name: 'Product Display NFC',
14554
+ * metadata: { productId: 'SKU-12345' }
14555
+ * });
14556
+ *
14557
+ * // Create a GPS geofence trigger source
14558
+ * const geoSource = await sdk.triggerSources.create({
14559
+ * type: 'GPS_GEOFENCE',
14560
+ * name: 'Store Area',
14561
+ * coordsLatitude: 47.6062,
14562
+ * coordsLongitude: -122.3321,
14563
+ * metadata: { radiusMeters: 100 }
14564
+ * });
14565
+ * ```
14566
+ */
14567
+ async create(triggerSource) {
14568
+ const result = await this.triggerSourceService.createTriggerSource(triggerSource);
14569
+ this.events?.emitSuccess({
14570
+ domain: 'trigger-source',
14571
+ type: 'TRIGGER_SOURCE_CREATED',
14572
+ userMessage: 'Trigger source created successfully',
14573
+ details: { triggerSourceId: result.id, type: result.type }
14574
+ });
14575
+ return result;
14576
+ }
14577
+ /**
14578
+ * Admin: Update a trigger source
14579
+ *
14580
+ * Updates an existing trigger source's configuration. All fields are optional;
14581
+ * only provided fields will be updated. Requires administrator privileges.
14582
+ *
14583
+ * @param triggerSourceId - UUID of the trigger source to update
14584
+ * @param triggerSource - Updated trigger source data (partial)
14585
+ * @returns Promise resolving to updated trigger source
14586
+ * @throws {PersApiError} When not authenticated as admin or trigger source not found
14587
+ *
14588
+ * @example
14589
+ * ```typescript
14590
+ * // Update trigger source name and location
14591
+ * const updated = await sdk.triggerSources.update('source-123', {
14592
+ * name: 'Updated Store Entrance QR',
14593
+ * description: 'New description',
14594
+ * coordsLatitude: 47.6063,
14595
+ * coordsLongitude: -122.3322
14596
+ * });
14597
+ * ```
14598
+ */
14599
+ async update(triggerSourceId, triggerSource) {
14600
+ const result = await this.triggerSourceService.updateTriggerSource(triggerSourceId, triggerSource);
14601
+ this.events?.emitSuccess({
14602
+ domain: 'trigger-source',
14603
+ type: 'TRIGGER_SOURCE_UPDATED',
14604
+ userMessage: 'Trigger source updated successfully',
14605
+ details: { triggerSourceId }
14606
+ });
14607
+ return result;
14608
+ }
14609
+ /**
14610
+ * Admin: Delete a trigger source
14611
+ *
14612
+ * Soft deletes a trigger source, making it inactive. The trigger source will
14613
+ * be removed from any campaigns it was assigned to. Requires administrator
14614
+ * privileges.
14615
+ *
14616
+ * @param triggerSourceId - UUID of the trigger source to delete
14617
+ * @returns Promise resolving to success status
14618
+ * @throws {PersApiError} When not authenticated as admin or trigger source not found
14619
+ *
14620
+ * @example
14621
+ * ```typescript
14622
+ * const success = await sdk.triggerSources.delete('source-123');
14623
+ * console.log('Trigger source deleted:', success);
14624
+ * ```
14625
+ */
14626
+ async delete(triggerSourceId) {
14627
+ const result = await this.triggerSourceService.deleteTriggerSource(triggerSourceId);
14628
+ this.events?.emitSuccess({
14629
+ domain: 'trigger-source',
14630
+ type: 'TRIGGER_SOURCE_DELETED',
14631
+ userMessage: 'Trigger source deleted successfully',
14632
+ details: { triggerSourceId }
14633
+ });
14634
+ return result;
14635
+ }
14636
+ /**
14637
+ * Get the full trigger source service for advanced operations
14638
+ *
14639
+ * @returns TriggerSourceService instance with full API access
14640
+ */
14641
+ getTriggerSourceService() {
14642
+ return this.triggerSourceService;
14643
+ }
14644
+ }
14645
+
13241
14646
  /**
13242
14647
  * @fileoverview PERS SDK - Platform-agnostic TypeScript SDK with High-Level Managers
13243
14648
  *
@@ -13351,78 +14756,71 @@ class PersSDK {
13351
14756
  // Initialize event emitter and wire to API client for error events
13352
14757
  this._events = new PersEventEmitter();
13353
14758
  this.apiClient.setEvents(this._events);
13354
- // Initialize domain managers (pass events to managers that emit success events)
13355
- this._auth = new AuthManager(this.apiClient, this._events);
13356
- this._users = new UserManager(this.apiClient, this._events);
13357
- this._userStatus = new UserStatusManager(this.apiClient);
13358
- this._tokens = new TokenManager(this.apiClient);
13359
- this._businesses = new BusinessManager(this.apiClient, this._events);
13360
- this._campaigns = new CampaignManager(this.apiClient, this._events);
13361
- this._redemptions = new RedemptionManager(this.apiClient, this._events);
13362
- this._transactions = new TransactionManager(this.apiClient, this._events);
13363
- this._purchases = new PurchaseManager(this.apiClient);
13364
- this._files = new FileManager(this.apiClient);
13365
- this._tenants = new TenantManager(this.apiClient);
13366
- this._apiKeys = new ApiKeyManager(this.apiClient);
13367
- this._analytics = new AnalyticsManager(this.apiClient);
13368
- this._donations = new DonationManager(this.apiClient);
13369
- // Automatically restore session if enabled and tokens exist
13370
- if (config.autoRestoreSession !== false) {
13371
- this.restoreSessionIfTokensExist().catch(err => {
13372
- console.warn('[PersSDK] Failed to restore session on initialization:', err);
13373
- });
13374
- }
13375
14759
  }
13376
14760
  /**
13377
- * Restore user session if valid tokens exist in storage
14761
+ * Restore user session from stored tokens
13378
14762
  *
13379
- * This method is called automatically during SDK initialization when
13380
- * `autoRestoreSession` is enabled (default). It checks for stored tokens
13381
- * and fetches user data to restore the authentication state.
14763
+ * Call this method after SDK initialization to restore the user's session
14764
+ * if valid tokens exist. This is useful for maintaining login state across
14765
+ * app restarts.
14766
+ *
14767
+ * **Important:** Only works for USER and BUSINESS accounts. Admin/tenant
14768
+ * accounts don't support user data fetching, so this will return null for them
14769
+ * (though their tokens remain valid for API calls).
13382
14770
  *
13383
14771
  * Emits auth domain success event when session is restored.
13384
14772
  *
13385
- * @internal
13386
- * @returns Promise that resolves when session is restored or determined to be invalid
14773
+ * @returns Promise resolving to User data if session restored, null if no session or admin account
14774
+ * @throws {PersError} If token validation or user fetch fails
14775
+ *
14776
+ * @example
14777
+ * ```typescript
14778
+ * // Call after initial render for better perceived performance
14779
+ * const user = await sdk.restoreSession();
14780
+ * if (user) {
14781
+ * console.log('Welcome back,', user.name);
14782
+ * }
14783
+ * ```
13387
14784
  */
13388
- async restoreSessionIfTokensExist() {
13389
- try {
13390
- const hasToken = await this._auth.hasValidAuth();
13391
- if (hasToken) {
13392
- console.log('[PersSDK] Valid tokens found, restoring session...');
13393
- try {
13394
- // Fetch user data to validate tokens and restore session
13395
- const userData = await this._auth.getCurrentUser();
13396
- console.log('[PersSDK] Session restored successfully');
13397
- // Emit event through proper event emitter
13398
- // Send minimal data (just userId) - consumers can fetch full user if needed
13399
- this._events.emitSuccess({
13400
- type: 'session_restored',
13401
- domain: 'authentication',
13402
- userMessage: 'Session restored successfully',
13403
- details: { userId: userData.id }
13404
- });
13405
- }
13406
- catch (error) {
13407
- // Tokens exist but are invalid (expired, revoked, etc.)
13408
- console.warn('[PersSDK] Failed to restore session, tokens may be invalid:', error);
13409
- await this._auth.clearAuth();
13410
- // Emit restoration failed event
13411
- this._events.emitError({
13412
- type: 'session_restoration_failed',
13413
- domain: 'authentication',
13414
- userMessage: 'Session restoration failed',
13415
- code: 'SESSION_INVALID',
13416
- details: { error }
13417
- });
13418
- }
13419
- }
13420
- else {
13421
- console.log('[PersSDK] No valid tokens found');
14785
+ async restoreSession() {
14786
+ const hasToken = await this.auth.hasValidAuth();
14787
+ if (!hasToken) {
14788
+ return null;
14789
+ }
14790
+ // Check auth type - only restore session for user and business accounts
14791
+ const authProvider = this.apiClient.getConfig().authProvider;
14792
+ if (authProvider?.getAuthType) {
14793
+ const authType = await authProvider.getAuthType();
14794
+ if (authType === exports.AccountOwnerType.TENANT) {
14795
+ // Admin sessions don't support getCurrentUser(), skip restoration
14796
+ // Tokens are valid and will be used for API calls
14797
+ return null;
13422
14798
  }
13423
14799
  }
14800
+ try {
14801
+ // Fetch user data to validate tokens and restore session
14802
+ const userData = await this.auth.getCurrentUser();
14803
+ // Emit event through proper event emitter
14804
+ this._events.emitSuccess({
14805
+ type: 'session_restored',
14806
+ domain: 'authentication',
14807
+ userMessage: 'Session restored successfully',
14808
+ details: { userId: userData.id }
14809
+ });
14810
+ return userData;
14811
+ }
13424
14812
  catch (error) {
13425
- console.warn('[PersSDK] Error during session restoration:', error);
14813
+ // Tokens exist but are invalid (expired, revoked, etc.)
14814
+ await this.auth.clearAuth();
14815
+ // Emit restoration failed event
14816
+ this._events.emitError({
14817
+ type: 'session_restoration_failed',
14818
+ domain: 'authentication',
14819
+ userMessage: 'Session restoration failed',
14820
+ code: 'SESSION_INVALID',
14821
+ details: { error }
14822
+ });
14823
+ throw error;
13426
14824
  }
13427
14825
  }
13428
14826
  /**
@@ -13511,6 +14909,9 @@ class PersSDK {
13511
14909
  * @see {@link AuthManager} for detailed documentation
13512
14910
  */
13513
14911
  get auth() {
14912
+ if (!this._auth) {
14913
+ this._auth = new AuthManager(this.apiClient, this._events);
14914
+ }
13514
14915
  return this._auth;
13515
14916
  }
13516
14917
  /**
@@ -13524,6 +14925,9 @@ class PersSDK {
13524
14925
  * ```
13525
14926
  */
13526
14927
  get users() {
14928
+ if (!this._users) {
14929
+ this._users = new UserManager(this.apiClient, this._events);
14930
+ }
13527
14931
  return this._users;
13528
14932
  }
13529
14933
  /**
@@ -13537,6 +14941,9 @@ class PersSDK {
13537
14941
  * ```
13538
14942
  */
13539
14943
  get userStatus() {
14944
+ if (!this._userStatus) {
14945
+ this._userStatus = new UserStatusManager(this.apiClient);
14946
+ }
13540
14947
  return this._userStatus;
13541
14948
  }
13542
14949
  /**
@@ -13550,6 +14957,9 @@ class PersSDK {
13550
14957
  * ```
13551
14958
  */
13552
14959
  get tokens() {
14960
+ if (!this._tokens) {
14961
+ this._tokens = new TokenManager(this.apiClient);
14962
+ }
13553
14963
  return this._tokens;
13554
14964
  }
13555
14965
  /**
@@ -13563,6 +14973,9 @@ class PersSDK {
13563
14973
  * ```
13564
14974
  */
13565
14975
  get businesses() {
14976
+ if (!this._businesses) {
14977
+ this._businesses = new BusinessManager(this.apiClient, this._events);
14978
+ }
13566
14979
  return this._businesses;
13567
14980
  }
13568
14981
  /**
@@ -13576,6 +14989,9 @@ class PersSDK {
13576
14989
  * ```
13577
14990
  */
13578
14991
  get campaigns() {
14992
+ if (!this._campaigns) {
14993
+ this._campaigns = new CampaignManager(this.apiClient, this._events);
14994
+ }
13579
14995
  return this._campaigns;
13580
14996
  }
13581
14997
  /**
@@ -13589,6 +15005,9 @@ class PersSDK {
13589
15005
  * ```
13590
15006
  */
13591
15007
  get redemptions() {
15008
+ if (!this._redemptions) {
15009
+ this._redemptions = new RedemptionManager(this.apiClient, this._events);
15010
+ }
13592
15011
  return this._redemptions;
13593
15012
  }
13594
15013
  /**
@@ -13602,6 +15021,9 @@ class PersSDK {
13602
15021
  * ```
13603
15022
  */
13604
15023
  get transactions() {
15024
+ if (!this._transactions) {
15025
+ this._transactions = new TransactionManager(this.apiClient, this._events);
15026
+ }
13605
15027
  return this._transactions;
13606
15028
  }
13607
15029
  /**
@@ -13615,6 +15037,9 @@ class PersSDK {
13615
15037
  * ```
13616
15038
  */
13617
15039
  get purchases() {
15040
+ if (!this._purchases) {
15041
+ this._purchases = new PurchaseManager(this.apiClient);
15042
+ }
13618
15043
  return this._purchases;
13619
15044
  }
13620
15045
  /**
@@ -13628,6 +15053,9 @@ class PersSDK {
13628
15053
  * ```
13629
15054
  */
13630
15055
  get files() {
15056
+ if (!this._files) {
15057
+ this._files = new FileManager(this.apiClient);
15058
+ }
13631
15059
  return this._files;
13632
15060
  }
13633
15061
  /**
@@ -13641,6 +15069,9 @@ class PersSDK {
13641
15069
  * ```
13642
15070
  */
13643
15071
  get tenants() {
15072
+ if (!this._tenants) {
15073
+ this._tenants = new TenantManager(this.apiClient);
15074
+ }
13644
15075
  return this._tenants;
13645
15076
  }
13646
15077
  /**
@@ -13665,6 +15096,9 @@ class PersSDK {
13665
15096
  * ```
13666
15097
  */
13667
15098
  get apiKeys() {
15099
+ if (!this._apiKeys) {
15100
+ this._apiKeys = new ApiKeyManager(this.apiClient);
15101
+ }
13668
15102
  return this._apiKeys;
13669
15103
  }
13670
15104
  /**
@@ -13676,6 +15110,9 @@ class PersSDK {
13676
15110
  * ```
13677
15111
  */
13678
15112
  get analytics() {
15113
+ if (!this._analytics) {
15114
+ this._analytics = new AnalyticsManager(this.apiClient);
15115
+ }
13679
15116
  return this._analytics;
13680
15117
  }
13681
15118
  /**
@@ -13687,8 +15124,45 @@ class PersSDK {
13687
15124
  * ```
13688
15125
  */
13689
15126
  get donations() {
15127
+ if (!this._donations) {
15128
+ this._donations = new DonationManager(this.apiClient);
15129
+ }
13690
15130
  return this._donations;
13691
15131
  }
15132
+ /**
15133
+ * TriggerSource manager - High-level trigger source operations (Admin Only)
15134
+ *
15135
+ * Provides CRUD operations for managing trigger sources (QR codes, NFC tags,
15136
+ * GPS geofences, API webhooks). TriggerSources are standalone entities that
15137
+ * can be assigned to campaigns via the campaigns manager.
15138
+ *
15139
+ * @returns TriggerSourceManager instance
15140
+ *
15141
+ * @example TriggerSource Operations
15142
+ * ```typescript
15143
+ * // Create a QR code trigger source
15144
+ * const qrSource = await sdk.triggerSources.create({
15145
+ * name: 'Store Entrance QR',
15146
+ * type: 'QR_CODE',
15147
+ * description: 'QR code at main entrance'
15148
+ * });
15149
+ *
15150
+ * // Get all trigger sources
15151
+ * const sources = await sdk.triggerSources.getAll();
15152
+ *
15153
+ * // Update trigger source
15154
+ * await sdk.triggerSources.update(sourceId, { name: 'Updated Name' });
15155
+ *
15156
+ * // Assign to campaign (via campaigns manager)
15157
+ * await sdk.campaigns.assignTriggerSource(campaignId, qrSource.id);
15158
+ * ```
15159
+ */
15160
+ get triggerSources() {
15161
+ if (!this._triggerSources) {
15162
+ this._triggerSources = new TriggerSourceManager(this.apiClient, this._events);
15163
+ }
15164
+ return this._triggerSources;
15165
+ }
13692
15166
  /**
13693
15167
  * Gets the API client for direct PERS API requests
13694
15168
  *
@@ -14581,11 +16055,19 @@ const SDKContext = react.createContext(null);
14581
16055
  // Provider component
14582
16056
  const PersSDKProvider = ({ children, config }) => {
14583
16057
  const initializingRef = react.useRef(false);
16058
+ // State refs for stable functions to read current values
16059
+ const sdkRef = react.useRef(null);
16060
+ const isInitializedRef = react.useRef(false);
16061
+ const isAuthenticatedRef = react.useRef(false);
14584
16062
  const [sdk, setSdk] = react.useState(null);
14585
16063
  const [authProvider, setAuthProvider] = react.useState(null);
14586
16064
  const [isInitialized, setIsInitialized] = react.useState(false);
14587
16065
  const [isAuthenticated, setIsAuthenticated] = react.useState(false);
14588
16066
  const [user, setUser] = react.useState(null);
16067
+ // Keep state refs in sync immediately (not in useEffect to avoid race conditions)
16068
+ sdkRef.current = sdk;
16069
+ isInitializedRef.current = isInitialized;
16070
+ isAuthenticatedRef.current = isAuthenticated;
14589
16071
  const initialize = react.useCallback(async (config) => {
14590
16072
  // Prevent multiple initializations
14591
16073
  if (isInitialized || initializingRef.current) {
@@ -14652,18 +16134,40 @@ const PersSDKProvider = ({ children, config }) => {
14652
16134
  }
14653
16135
  }, [config, isInitialized, initialize]);
14654
16136
  const refreshUserData = react.useCallback(async () => {
14655
- if (!sdk || !isAuthenticated || !isInitialized) {
14656
- throw new Error('SDK not initialized or not authenticated. Cannot refresh user data.');
16137
+ // Read from refs to get current values
16138
+ const currentSdk = sdkRef.current;
16139
+ const currentIsInitialized = isInitializedRef.current;
16140
+ if (!currentSdk || !currentIsInitialized) {
16141
+ throw new Error('SDK not initialized. Cannot refresh user data.');
14657
16142
  }
14658
16143
  try {
14659
- const freshUserData = await sdk.users.getCurrentUser();
16144
+ const freshUserData = await currentSdk.users.getCurrentUser();
14660
16145
  setUser(freshUserData);
14661
16146
  }
14662
16147
  catch (error) {
14663
16148
  console.error('Failed to refresh user data:', error);
14664
16149
  throw error;
14665
16150
  }
14666
- }, [sdk, isAuthenticated, isInitialized]);
16151
+ }, []); // No dependencies - reads from refs
16152
+ const restoreSession = react.useCallback(async () => {
16153
+ // Read from refs to get current values
16154
+ const currentSdk = sdkRef.current;
16155
+ const currentIsInitialized = isInitializedRef.current;
16156
+ if (!currentSdk || !currentIsInitialized) {
16157
+ throw new Error('SDK not initialized. Call initialize() first.');
16158
+ }
16159
+ try {
16160
+ const userData = await currentSdk.restoreSession();
16161
+ if (userData) {
16162
+ setAuthenticationState(userData, true);
16163
+ }
16164
+ return userData;
16165
+ }
16166
+ catch (error) {
16167
+ console.error('[PersSDK] Failed to restore session:', error);
16168
+ throw error;
16169
+ }
16170
+ }, [setAuthenticationState]); // Depends on setAuthenticationState
14667
16171
  // Listen for authentication events from core SDK
14668
16172
  // Set up immediately when SDK is created (don't wait for isInitialized)
14669
16173
  // to catch session_restored events that fire during SDK initialization
@@ -14676,13 +16180,18 @@ const PersSDKProvider = ({ children, config }) => {
14676
16180
  // Session restored successfully - sync React state
14677
16181
  if (event.type === 'session_restored') {
14678
16182
  console.log('[PersSDK] Session restoration event received, syncing state...');
14679
- sdk.users.getCurrentUser()
14680
- .then(userData => {
14681
- setAuthenticationState(userData, true);
14682
- })
14683
- .catch(error => {
14684
- console.error('[PersSDK] Failed to sync restored session:', error);
14685
- });
16183
+ // Read user from event details if available, otherwise fetch
16184
+ const userId = event.details?.userId;
16185
+ if (userId) {
16186
+ // User ID available, fetch user data
16187
+ sdk.users.getCurrentUser()
16188
+ .then(userData => {
16189
+ setAuthenticationState(userData, true);
16190
+ })
16191
+ .catch(error => {
16192
+ console.error('[PersSDK] Failed to sync restored session:', error);
16193
+ });
16194
+ }
14686
16195
  }
14687
16196
  // Session restoration failed or auth error - clear React state
14688
16197
  if (event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
@@ -14720,28 +16229,17 @@ const PersSDKProvider = ({ children, config }) => {
14720
16229
  const contextValue = react.useMemo(() => ({
14721
16230
  // Main SDK instance
14722
16231
  sdk,
14723
- // Manager shortcuts for convenience
14724
- auth: sdk?.auth || null,
14725
- users: sdk?.users || null,
14726
- tokens: sdk?.tokens || null,
14727
- businesses: sdk?.businesses || null,
14728
- campaigns: sdk?.campaigns || null,
14729
- redemptions: sdk?.redemptions || null,
14730
- transactions: sdk?.transactions || null,
14731
- purchases: sdk?.purchases || null,
14732
- tenants: sdk?.tenants || null,
14733
- analytics: sdk?.analytics || null,
14734
- donations: sdk?.donations || null,
14735
16232
  // Platform-specific providers
14736
16233
  authProvider,
14737
16234
  // State
14738
16235
  isInitialized,
14739
16236
  isAuthenticated,
14740
16237
  user,
14741
- // Methods
16238
+ // Methods - expose functions directly, not through refs
14742
16239
  initialize,
14743
16240
  setAuthenticationState,
14744
16241
  refreshUserData,
16242
+ restoreSession,
14745
16243
  }), [
14746
16244
  sdk,
14747
16245
  authProvider,
@@ -14750,7 +16248,8 @@ const PersSDKProvider = ({ children, config }) => {
14750
16248
  user,
14751
16249
  initialize,
14752
16250
  setAuthenticationState,
14753
- refreshUserData
16251
+ refreshUserData,
16252
+ restoreSession
14754
16253
  ]);
14755
16254
  return (jsxRuntime.jsx(SDKContext.Provider, { value: contextValue, children: children }));
14756
16255
  };
@@ -36091,7 +37590,7 @@ const useWeb3 = () => {
36091
37590
  */
36092
37591
  function useTokenBalances(options) {
36093
37592
  const { accountAddress, availableTokens = [], autoLoad = true, refreshInterval = 0 } = options;
36094
- const { isAuthenticated } = usePersSDK();
37593
+ const { isAuthenticated, sdk } = usePersSDK();
36095
37594
  const web3 = useWeb3();
36096
37595
  const [tokenBalances, setTokenBalances] = react.useState([]);
36097
37596
  const [isLoading, setIsLoading] = react.useState(false);
@@ -36193,15 +37692,28 @@ function useTokenBalances(options) {
36193
37692
  loadBalances();
36194
37693
  }
36195
37694
  }, [autoLoad, isAvailable, availableTokens.length, loadBalances]);
36196
- // Optional auto-refresh interval
37695
+ // Event-driven refresh: listen for transaction events instead of polling
36197
37696
  react.useEffect(() => {
36198
- if (refreshInterval > 0 && isAvailable && availableTokens.length > 0) {
36199
- const intervalId = setInterval(() => {
37697
+ if (!sdk || refreshInterval <= 0 || !isAvailable)
37698
+ return;
37699
+ // Subscribe to transaction domain events
37700
+ const unsubscribe = sdk.events.subscribe((event) => {
37701
+ if (event.domain === 'transaction' && event.type === 'transaction_completed') {
37702
+ console.log('[useTokenBalances] Transaction completed, refreshing balances...');
36200
37703
  loadBalances();
36201
- }, refreshInterval);
36202
- return () => clearInterval(intervalId);
36203
- }
36204
- }, [refreshInterval, isAvailable, availableTokens.length, loadBalances]);
37704
+ }
37705
+ }, { domains: ['transaction'] });
37706
+ // Also set up a fallback polling interval (much longer than before)
37707
+ // This handles cases where events might be missed
37708
+ const fallbackInterval = Math.max(refreshInterval, 60000); // Minimum 1 minute
37709
+ const intervalId = setInterval(() => {
37710
+ loadBalances();
37711
+ }, fallbackInterval);
37712
+ return () => {
37713
+ unsubscribe();
37714
+ clearInterval(intervalId);
37715
+ };
37716
+ }, [sdk, refreshInterval, isAvailable, loadBalances]);
36205
37717
  return {
36206
37718
  tokenBalances,
36207
37719
  isLoading,
@@ -36849,25 +38361,32 @@ const useTransactions = () => {
36849
38361
  }
36850
38362
  }, [sdk, isInitialized, signAndSubmitTransactionWithJWT, isSignerAvailable]);
36851
38363
  /**
36852
- * Retrieves a specific transaction by its ID
38364
+ * Retrieves a specific transaction by its ID with optional include relations
36853
38365
  *
36854
38366
  * @param transactionId - Unique identifier of the transaction
38367
+ * @param include - Optional relations to include (sender, recipient, business) for enriched entity data
36855
38368
  * @returns Promise resolving to transaction data or null if not found
36856
38369
  * @throws Error if SDK is not initialized
36857
38370
  *
36858
38371
  * @example
36859
38372
  * ```typescript
36860
38373
  * const { getTransactionById } = useTransactions();
38374
+ *
38375
+ * // Basic retrieval
36861
38376
  * const transaction = await getTransactionById('txn-123');
36862
- * console.log('Transaction details:', transaction);
38377
+ *
38378
+ * // With enriched sender/recipient data
38379
+ * const enrichedTx = await getTransactionById('txn-123', ['sender', 'recipient', 'business']);
38380
+ * console.log('Sender:', enrichedTx?.included?.sender);
38381
+ * console.log('Business:', enrichedTx?.included?.engagedBusiness?.displayName);
36863
38382
  * ```
36864
38383
  */
36865
- const getTransactionById = react.useCallback(async (transactionId) => {
38384
+ const getTransactionById = react.useCallback(async (transactionId, include) => {
36866
38385
  if (!isInitialized || !sdk) {
36867
38386
  throw new Error('SDK not initialized. Call initialize() first.');
36868
38387
  }
36869
38388
  try {
36870
- const result = await sdk.transactions.getTransactionById(transactionId);
38389
+ const result = await sdk.transactions.getTransactionById(transactionId, include);
36871
38390
  return result;
36872
38391
  }
36873
38392
  catch (error) {
@@ -36876,25 +38395,48 @@ const useTransactions = () => {
36876
38395
  }
36877
38396
  }, [sdk, isInitialized]);
36878
38397
  /**
36879
- * Retrieves transaction history for the authenticated user, filtered by role
38398
+ * Retrieves transaction history for the authenticated user with comprehensive filtering
36880
38399
  *
36881
- * @param role - Optional transaction role filter (TransactionRole.SENDER, TransactionRole.RECIPIENT)
36882
- * @returns Promise resolving to array of user's transactions
38400
+ * Supports filtering by role, status, type, business, token, and more.
38401
+ * Optionally enrich with related entities (sender, recipient, business).
38402
+ *
38403
+ * @param options - Query options including filters, pagination, and include relations
38404
+ * @returns Promise resolving to paginated array of user's transactions
36883
38405
  * @throws Error if SDK is not initialized
36884
38406
  *
36885
38407
  * @example
36886
38408
  * ```typescript
36887
38409
  * const { getUserTransactionHistory } = useTransactions();
36888
- * const sentTransactions = await getUserTransactionHistory(TransactionRole.SENDER);
36889
- * const allTransactions = await getUserTransactionHistory(); // No filter
38410
+ *
38411
+ * // Simple: Get all transactions
38412
+ * const allTransactions = await getUserTransactionHistory();
38413
+ *
38414
+ * // Filter by role (legacy support)
38415
+ * const sentTransactions = await getUserTransactionHistory({ role: 'SENDER' });
38416
+ *
38417
+ * // Advanced filtering with include relations
38418
+ * const filtered = await getUserTransactionHistory({
38419
+ * role: 'SENDER',
38420
+ * status: 'COMPLETED',
38421
+ * engagedBusinessId: 'business-123',
38422
+ * include: ['recipient', 'business'],
38423
+ * page: 1,
38424
+ * limit: 20
38425
+ * });
38426
+ *
38427
+ * // Access enriched data
38428
+ * filtered.data.forEach(tx => {
38429
+ * console.log('Recipient:', tx.included?.recipient);
38430
+ * console.log('Business:', tx.included?.engagedBusiness?.displayName);
38431
+ * });
36890
38432
  * ```
36891
38433
  */
36892
- const getUserTransactionHistory = react.useCallback(async (role) => {
38434
+ const getUserTransactionHistory = react.useCallback(async (options) => {
36893
38435
  if (!isInitialized || !sdk) {
36894
38436
  throw new Error('SDK not initialized. Call initialize() first.');
36895
38437
  }
36896
38438
  try {
36897
- const result = await sdk.transactions.getUserTransactionHistory(role);
38439
+ const result = await sdk.transactions.getUserTransactionHistory(options);
36898
38440
  return result;
36899
38441
  }
36900
38442
  catch (error) {
@@ -36902,25 +38444,39 @@ const useTransactions = () => {
36902
38444
  throw error;
36903
38445
  }
36904
38446
  }, [sdk, isInitialized]);
36905
- const getTenantTransactions = react.useCallback(async () => {
36906
- if (!isInitialized || !sdk) {
36907
- throw new Error('SDK not initialized. Call initialize() first.');
36908
- }
36909
- try {
36910
- const result = await sdk.transactions.getTenantTransactions();
36911
- return result;
36912
- }
36913
- catch (error) {
36914
- console.error('Failed to fetch tenant transactions:', error);
36915
- throw error;
36916
- }
36917
- }, [sdk, isInitialized]);
36918
- const getPaginatedTransactions = react.useCallback(async (params) => {
38447
+ /**
38448
+ * Admin: Get paginated transactions with optional include relations
38449
+ *
38450
+ * @param params - Pagination and filtering parameters
38451
+ * @param include - Optional relations to include for enriched entity data
38452
+ * @returns Promise resolving to paginated transaction results
38453
+ * @throws Error if SDK is not initialized
38454
+ *
38455
+ * @example
38456
+ * ```typescript
38457
+ * const { getPaginatedTransactions } = useTransactions();
38458
+ *
38459
+ * // Basic pagination
38460
+ * const result = await getPaginatedTransactions({ page: 1, limit: 50 });
38461
+ *
38462
+ * // With include relations
38463
+ * const enrichedResult = await getPaginatedTransactions(
38464
+ * { page: 1, limit: 50, sortBy: 'createdAt', sortOrder: 'DESC' },
38465
+ * include: ['sender', 'recipient', 'business']
38466
+ * });
38467
+ *
38468
+ * enrichedResult.data.forEach(tx => {
38469
+ * console.log('From:', tx.included?.sender);
38470
+ * console.log('To:', tx.included?.recipient);
38471
+ * });
38472
+ * ```
38473
+ */
38474
+ const getPaginatedTransactions = react.useCallback(async (options) => {
36919
38475
  if (!isInitialized || !sdk) {
36920
38476
  throw new Error('SDK not initialized. Call initialize() first.');
36921
38477
  }
36922
38478
  try {
36923
- const result = await sdk.transactions.getPaginatedTransactions(params);
38479
+ const result = await sdk.transactions.getPaginatedTransactions(options);
36924
38480
  return result;
36925
38481
  }
36926
38482
  catch (error) {
@@ -36945,7 +38501,6 @@ const useTransactions = () => {
36945
38501
  createTransaction,
36946
38502
  getTransactionById,
36947
38503
  getUserTransactionHistory,
36948
- getTenantTransactions,
36949
38504
  getPaginatedTransactions,
36950
38505
  exportTransactionsCSV,
36951
38506
  isAvailable: isInitialized && !!sdk?.transactions,
@@ -37178,12 +38733,26 @@ const useCampaigns = () => {
37178
38733
  throw error;
37179
38734
  }
37180
38735
  }, [sdk, isInitialized]);
37181
- const getCampaignById = react.useCallback(async (campaignId) => {
38736
+ /**
38737
+ * Get campaign by ID with optional include relations
38738
+ *
38739
+ * @param campaignId - The campaign ID
38740
+ * @param include - Relations to include: 'triggerSources', 'businesses'
38741
+ * @returns Promise resolving to campaign data
38742
+ *
38743
+ * @example
38744
+ * ```typescript
38745
+ * // Get campaign with all related data
38746
+ * const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
38747
+ * console.log('Trigger sources:', campaign.included?.triggerSources);
38748
+ * ```
38749
+ */
38750
+ const getCampaignById = react.useCallback(async (campaignId, include) => {
37182
38751
  if (!isInitialized || !sdk) {
37183
38752
  throw new Error('SDK not initialized. Call initialize() first.');
37184
38753
  }
37185
38754
  try {
37186
- const result = await sdk.campaigns.getCampaignById(campaignId);
38755
+ const result = await sdk.campaigns.getCampaignById(campaignId, include);
37187
38756
  return result;
37188
38757
  }
37189
38758
  catch (error) {
@@ -37207,7 +38776,25 @@ const useCampaigns = () => {
37207
38776
  throw error;
37208
38777
  }
37209
38778
  }, [sdk, isInitialized, isAuthenticated]);
37210
- const getUserClaims = react.useCallback(async () => {
38779
+ /**
38780
+ * Get user's campaign claims with optional pagination and include relations
38781
+ *
38782
+ * @param options - Optional options including pagination and include relations
38783
+ * @returns Promise resolving to paginated campaign claims
38784
+ *
38785
+ * @example
38786
+ * ```typescript
38787
+ * // Get user's claims with campaign details
38788
+ * const { data: claims } = await getUserClaims({ include: ['campaign', 'business'] });
38789
+ * claims.forEach(claim => {
38790
+ * console.log('Campaign:', claim.included?.campaign?.title);
38791
+ * });
38792
+ *
38793
+ * // With pagination
38794
+ * const { data } = await getUserClaims({ page: 1, limit: 10, include: ['campaign'] });
38795
+ * ```
38796
+ */
38797
+ const getUserClaims = react.useCallback(async (options) => {
37211
38798
  if (!isInitialized || !sdk) {
37212
38799
  throw new Error('SDK not initialized. Call initialize() first.');
37213
38800
  }
@@ -37216,7 +38803,7 @@ const useCampaigns = () => {
37216
38803
  return { data: [], pagination: { page: 1, limit: 0, total: 0, pages: 0, hasNext: false, hasPrev: false } };
37217
38804
  }
37218
38805
  try {
37219
- const result = await sdk.campaigns.getUserClaims();
38806
+ const result = await sdk.campaigns.getUserClaims(options);
37220
38807
  return result;
37221
38808
  }
37222
38809
  catch (error) {
@@ -37251,12 +38838,28 @@ const useCampaigns = () => {
37251
38838
  throw error;
37252
38839
  }
37253
38840
  }, [sdk, isInitialized]);
37254
- const getCampaignClaims = react.useCallback(async () => {
38841
+ /**
38842
+ * Admin: Get campaign claims with optional filters and include relations
38843
+ *
38844
+ * @param filters - Optional filters for campaign, user, or business
38845
+ * @param include - Relations to include: 'campaign', 'user', 'business'
38846
+ * @returns Promise resolving to paginated campaign claims
38847
+ *
38848
+ * @example
38849
+ * ```typescript
38850
+ * // Get all claims with user details
38851
+ * const { data: claims } = await getCampaignClaims({}, ['user']);
38852
+ *
38853
+ * // Get claims for a specific campaign
38854
+ * const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
38855
+ * ```
38856
+ */
38857
+ const getCampaignClaims = react.useCallback(async (filters, include) => {
37255
38858
  if (!isInitialized || !sdk) {
37256
38859
  throw new Error('SDK not initialized. Call initialize() first.');
37257
38860
  }
37258
38861
  try {
37259
- const result = await sdk.campaigns.getCampaignClaims();
38862
+ const result = await sdk.campaigns.getCampaignClaims(filters, include);
37260
38863
  return result;
37261
38864
  }
37262
38865
  catch (error) {
@@ -37264,12 +38867,19 @@ const useCampaigns = () => {
37264
38867
  throw error;
37265
38868
  }
37266
38869
  }, [sdk, isInitialized]);
37267
- const getCampaignClaimsByUserId = react.useCallback(async (userId) => {
38870
+ /**
38871
+ * Admin: Get campaign claims by user ID with optional include relations
38872
+ *
38873
+ * @param userId - The user ID
38874
+ * @param include - Relations to include: 'campaign', 'user', 'business'
38875
+ * @returns Promise resolving to paginated campaign claims
38876
+ */
38877
+ const getCampaignClaimsByUserId = react.useCallback(async (userId, include) => {
37268
38878
  if (!isInitialized || !sdk) {
37269
38879
  throw new Error('SDK not initialized. Call initialize() first.');
37270
38880
  }
37271
38881
  try {
37272
- const result = await sdk.campaigns.getCampaignClaimsByUserId(userId);
38882
+ const result = await sdk.campaigns.getCampaignClaimsByUserId(userId, undefined, include);
37273
38883
  return result;
37274
38884
  }
37275
38885
  catch (error) {
@@ -37277,12 +38887,19 @@ const useCampaigns = () => {
37277
38887
  throw error;
37278
38888
  }
37279
38889
  }, [sdk, isInitialized]);
37280
- const getCampaignClaimsByBusinessId = react.useCallback(async (businessId) => {
38890
+ /**
38891
+ * Admin: Get campaign claims by business ID with optional include relations
38892
+ *
38893
+ * @param businessId - The business ID
38894
+ * @param include - Relations to include: 'campaign', 'user', 'business'
38895
+ * @returns Promise resolving to paginated campaign claims
38896
+ */
38897
+ const getCampaignClaimsByBusinessId = react.useCallback(async (businessId, include) => {
37281
38898
  if (!isInitialized || !sdk) {
37282
38899
  throw new Error('SDK not initialized. Call initialize() first.');
37283
38900
  }
37284
38901
  try {
37285
- const result = await sdk.campaigns.getCampaignClaimsByBusinessId(businessId);
38902
+ const result = await sdk.campaigns.getCampaignClaimsByBusinessId(businessId, undefined, include);
37286
38903
  return result;
37287
38904
  }
37288
38905
  catch (error) {
@@ -37290,6 +38907,76 @@ const useCampaigns = () => {
37290
38907
  throw error;
37291
38908
  }
37292
38909
  }, [sdk, isInitialized]);
38910
+ // ==========================================
38911
+ // TRIGGER SOURCE ASSIGNMENT (Admin)
38912
+ // Note: TriggerSource CRUD is in useTriggerSources hook
38913
+ // ==========================================
38914
+ /**
38915
+ * Admin: Assign a trigger source to a campaign
38916
+ *
38917
+ * Associates a trigger source (QR code, NFC tag, etc.) with a campaign.
38918
+ * A campaign can have multiple trigger sources.
38919
+ *
38920
+ * Note: To create/update/delete trigger sources, use `useTriggerSources` hook.
38921
+ *
38922
+ * @param campaignId - Campaign UUID
38923
+ * @param triggerSourceId - Trigger source UUID
38924
+ * @returns Promise resolving to updated campaign
38925
+ *
38926
+ * @example
38927
+ * ```typescript
38928
+ * const { create } = useTriggerSources();
38929
+ * const { assignTriggerSource } = useCampaigns();
38930
+ *
38931
+ * // Create trigger source first
38932
+ * const source = await create({ type: 'QR_CODE', name: 'Store QR' });
38933
+ *
38934
+ * // Then assign to campaign
38935
+ * const updated = await assignTriggerSource('campaign-123', source.id);
38936
+ * ```
38937
+ */
38938
+ const assignTriggerSource = react.useCallback(async (campaignId, triggerSourceId) => {
38939
+ if (!isInitialized || !sdk) {
38940
+ throw new Error('SDK not initialized. Call initialize() first.');
38941
+ }
38942
+ if (!isAuthenticated) {
38943
+ throw new Error('SDK not authenticated. assignTriggerSource requires admin authentication.');
38944
+ }
38945
+ try {
38946
+ const result = await sdk.campaigns.assignTriggerSource(campaignId, triggerSourceId);
38947
+ return result;
38948
+ }
38949
+ catch (error) {
38950
+ console.error('Failed to assign trigger source:', error);
38951
+ throw error;
38952
+ }
38953
+ }, [sdk, isInitialized, isAuthenticated]);
38954
+ /**
38955
+ * Admin: Remove a trigger source from a campaign
38956
+ *
38957
+ * Removes the association between a trigger source and a campaign.
38958
+ * The trigger source itself is not deleted.
38959
+ *
38960
+ * @param campaignId - Campaign UUID
38961
+ * @param triggerSourceId - Trigger source UUID
38962
+ * @returns Promise resolving to updated campaign
38963
+ */
38964
+ const removeTriggerSource = react.useCallback(async (campaignId, triggerSourceId) => {
38965
+ if (!isInitialized || !sdk) {
38966
+ throw new Error('SDK not initialized. Call initialize() first.');
38967
+ }
38968
+ if (!isAuthenticated) {
38969
+ throw new Error('SDK not authenticated. removeTriggerSource requires admin authentication.');
38970
+ }
38971
+ try {
38972
+ const result = await sdk.campaigns.removeTriggerSource(campaignId, triggerSourceId);
38973
+ return result;
38974
+ }
38975
+ catch (error) {
38976
+ console.error('Failed to remove trigger source:', error);
38977
+ throw error;
38978
+ }
38979
+ }, [sdk, isInitialized, isAuthenticated]);
37293
38980
  return {
37294
38981
  getActiveCampaigns,
37295
38982
  getCampaignById,
@@ -37300,6 +38987,8 @@ const useCampaigns = () => {
37300
38987
  getCampaignClaims,
37301
38988
  getCampaignClaimsByUserId,
37302
38989
  getCampaignClaimsByBusinessId,
38990
+ assignTriggerSource,
38991
+ removeTriggerSource,
37303
38992
  isAvailable: isInitialized && !!sdk?.campaigns,
37304
38993
  };
37305
38994
  };
@@ -37328,7 +39017,23 @@ const useRedemptions = () => {
37328
39017
  throw error;
37329
39018
  }
37330
39019
  }, [sdk, isInitialized]);
37331
- const getUserRedemptions = react.useCallback(async () => {
39020
+ /**
39021
+ * Get user's redemption history with optional include relations
39022
+ *
39023
+ * @param include - Relations to include: 'redemption', 'user', 'business'
39024
+ * @returns Promise resolving to paginated redemption history
39025
+ *
39026
+ * @example
39027
+ * ```typescript
39028
+ * // Get user redemptions with full redemption details
39029
+ * const { data: redeems } = await getUserRedemptions(['redemption', 'business']);
39030
+ * redeems.forEach(redeem => {
39031
+ * console.log('Redemption:', redeem.included?.redemption?.title);
39032
+ * console.log('Business:', redeem.included?.business?.displayName);
39033
+ * });
39034
+ * ```
39035
+ */
39036
+ const getUserRedemptions = react.useCallback(async (include) => {
37332
39037
  if (!isInitialized || !sdk) {
37333
39038
  throw new Error('SDK not initialized. Call initialize() first.');
37334
39039
  }
@@ -37337,7 +39042,7 @@ const useRedemptions = () => {
37337
39042
  return { data: [], pagination: { page: 1, limit: 0, total: 0, pages: 0, hasNext: false, hasPrev: false } };
37338
39043
  }
37339
39044
  try {
37340
- const result = await sdk.redemptions.getUserRedemptions();
39045
+ const result = await sdk.redemptions.getUserRedemptions(undefined, include);
37341
39046
  return result;
37342
39047
  }
37343
39048
  catch (error) {
@@ -37391,6 +39096,40 @@ const useRedemptions = () => {
37391
39096
  }
37392
39097
  }, [sdk, isInitialized, isAuthenticated, signAndSubmitTransactionWithJWT, isSignerAvailable]);
37393
39098
  // Admin methods
39099
+ /**
39100
+ * Admin: Get all redemption redeems with filters and include relations
39101
+ *
39102
+ * Retrieves all redemption redeems across the platform with filtering.
39103
+ *
39104
+ * @param filters - Optional filters for user, redemption, and pagination
39105
+ * @param include - Relations to include: 'redemption', 'user', 'business'
39106
+ * @returns Promise resolving to paginated redemption redeems
39107
+ *
39108
+ * @example
39109
+ * ```typescript
39110
+ * // Get all redeems with user details
39111
+ * const { data: redeems } = await getRedemptionRedeems({}, ['user', 'redemption']);
39112
+ *
39113
+ * // Filter by specific user
39114
+ * const { data: userRedeems } = await getRedemptionRedeems(
39115
+ * { userId: 'user-123' },
39116
+ * ['redemption']
39117
+ * );
39118
+ * ```
39119
+ */
39120
+ const getRedemptionRedeems = react.useCallback(async (filters, include) => {
39121
+ if (!isInitialized || !sdk) {
39122
+ throw new Error('SDK not initialized. Call initialize() first.');
39123
+ }
39124
+ try {
39125
+ const result = await sdk.redemptions.getRedemptionRedeems(filters, include);
39126
+ return result;
39127
+ }
39128
+ catch (error) {
39129
+ console.error('Failed to fetch redemption redeems:', error);
39130
+ throw error;
39131
+ }
39132
+ }, [sdk, isInitialized]);
37394
39133
  const createRedemption = react.useCallback(async (redemptionData) => {
37395
39134
  if (!isInitialized || !sdk) {
37396
39135
  throw new Error('SDK not initialized. Call initialize() first.');
@@ -37448,6 +39187,7 @@ const useRedemptions = () => {
37448
39187
  getUserRedemptions,
37449
39188
  redeem,
37450
39189
  getRedemptionTypes,
39190
+ getRedemptionRedeems,
37451
39191
  createRedemption,
37452
39192
  updateRedemption,
37453
39193
  toggleRedemptionStatus,
@@ -37876,29 +39616,47 @@ const useFiles = () => {
37876
39616
  /**
37877
39617
  * React hook for analytics operations in the PERS SDK
37878
39618
  *
37879
- * Provides methods for retrieving transaction analytics and insights.
37880
- * Supports various analytics queries for business intelligence and reporting.
39619
+ * Provides comprehensive analytics and business intelligence capabilities:
39620
+ * - **Transaction Analytics**: Volume, trends, business performance
39621
+ * - **Campaign Claim Analytics**: Campaign performance and claim patterns
39622
+ * - **User Analytics**: Engagement metrics with per-active-user averages
39623
+ * - **User Ranking**: Leaderboards with full user details
39624
+ * - **Business Ranking**: Business performance rankings
39625
+ * - **Retention Analytics**: Monthly retention metrics
37881
39626
  *
37882
39627
  * @returns Analytics hook with methods for data analysis
37883
39628
  *
37884
- * @example
39629
+ * @example Basic Transaction Analytics
37885
39630
  * ```typescript
37886
39631
  * function AnalyticsComponent() {
37887
39632
  * const { getTransactionAnalytics } = useAnalytics();
37888
39633
  *
37889
39634
  * const loadAnalytics = async () => {
37890
- * try {
37891
- * const analytics = await getTransactionAnalytics({
37892
- * timeRange: 'last_30_days',
37893
- * groupBy: 'day'
37894
- * });
37895
- * console.log('Transaction analytics:', analytics);
37896
- * } catch (error) {
37897
- * console.error('Failed to load analytics:', error);
37898
- * }
39635
+ * const analytics = await getTransactionAnalytics({
39636
+ * startDate: '2024-01-01',
39637
+ * endDate: '2024-01-31',
39638
+ * groupBy: 'day'
39639
+ * });
39640
+ * console.log('Transaction analytics:', analytics);
37899
39641
  * };
39642
+ * }
39643
+ * ```
37900
39644
  *
37901
- * return <button onClick={loadAnalytics}>Load Analytics</button>;
39645
+ * @example User Leaderboard
39646
+ * ```typescript
39647
+ * function LeaderboardScreen() {
39648
+ * const { getUserRanking } = useAnalytics();
39649
+ *
39650
+ * const loadLeaderboard = async () => {
39651
+ * const ranking = await getUserRanking({
39652
+ * sortBy: 'totalTransactions',
39653
+ * sortOrder: 'DESC',
39654
+ * limit: 50
39655
+ * });
39656
+ * ranking.results.forEach((user, i) => {
39657
+ * console.log(`#${i + 1}: ${user.email} - ${user.totalTransactions} txns`);
39658
+ * });
39659
+ * };
37902
39660
  * }
37903
39661
  * ```
37904
39662
  */
@@ -37913,16 +39671,12 @@ const useAnalytics = () => {
37913
39671
  *
37914
39672
  * @example
37915
39673
  * ```typescript
37916
- * const { getTransactionAnalytics } = useAnalytics();
37917
39674
  * const analytics = await getTransactionAnalytics({
37918
- * groupBy: ['day'],
37919
- * metrics: ['count', 'sum'],
37920
39675
  * startDate: '2024-01-01',
37921
39676
  * endDate: '2024-01-31',
37922
- * filters: { status: 'completed' }
39677
+ * groupBy: 'day',
39678
+ * metrics: ['count', 'sum']
37923
39679
  * });
37924
- * console.log('Daily transaction analytics:', analytics.results);
37925
- * console.log('Execution time:', analytics.metadata.executionTime);
37926
39680
  * ```
37927
39681
  */
37928
39682
  const getTransactionAnalytics = react.useCallback(async (request) => {
@@ -37938,8 +39692,214 @@ const useAnalytics = () => {
37938
39692
  throw error;
37939
39693
  }
37940
39694
  }, [sdk, isInitialized]);
39695
+ /**
39696
+ * Retrieves campaign claim analytics with aggregation
39697
+ *
39698
+ * Provides insights into campaign performance, claim patterns, and user engagement.
39699
+ *
39700
+ * @param request - Analytics request with filters, groupBy, and metrics
39701
+ * @returns Promise resolving to campaign claim analytics data
39702
+ *
39703
+ * @example Claims per campaign
39704
+ * ```typescript
39705
+ * const analytics = await getCampaignClaimAnalytics({
39706
+ * filters: { status: 'COMPLETED' },
39707
+ * groupBy: ['campaignId'],
39708
+ * metrics: ['count'],
39709
+ * sortBy: 'count',
39710
+ * sortOrder: 'DESC',
39711
+ * limit: 10
39712
+ * });
39713
+ * console.log('Top campaigns:', analytics.results);
39714
+ * ```
39715
+ */
39716
+ const getCampaignClaimAnalytics = react.useCallback(async (request) => {
39717
+ if (!isInitialized || !sdk) {
39718
+ throw new Error('SDK not initialized. Call initialize() first.');
39719
+ }
39720
+ try {
39721
+ const result = await sdk.analytics.getCampaignClaimAnalytics(request);
39722
+ return result;
39723
+ }
39724
+ catch (error) {
39725
+ console.error('Failed to fetch campaign claim analytics:', error);
39726
+ throw error;
39727
+ }
39728
+ }, [sdk, isInitialized]);
39729
+ /**
39730
+ * Retrieves user analytics with engagement metrics
39731
+ *
39732
+ * Includes both per-user and per-active-user metrics for accurate engagement insights.
39733
+ * Per-active-user metrics show concentrated engagement among active users.
39734
+ *
39735
+ * @param request - Analytics request with optional filters and date range
39736
+ * @returns Promise resolving to user analytics data
39737
+ *
39738
+ * @example Compare per-user vs per-active-user metrics
39739
+ * ```typescript
39740
+ * const analytics = await getUserAnalytics({
39741
+ * startDate: new Date('2026-02-01'),
39742
+ * endDate: new Date('2026-02-28')
39743
+ * });
39744
+ *
39745
+ * console.log(`Active users: ${analytics.activeUsers} / ${analytics.totalUsers}`);
39746
+ * console.log(`Engagement rate: ${analytics.engagementRate.toFixed(1)}%`);
39747
+ *
39748
+ * // Per-active-user metrics (more meaningful)
39749
+ * console.log(`Avg transactions per active user: ${analytics.averageTransactionsPerActiveUser}`);
39750
+ * ```
39751
+ *
39752
+ * @example Business-specific analytics
39753
+ * ```typescript
39754
+ * const analytics = await getUserAnalytics({
39755
+ * filters: { businessId: 'business-123' },
39756
+ * startDate: new Date('2026-01-01'),
39757
+ * endDate: new Date('2026-12-31')
39758
+ * });
39759
+ * ```
39760
+ */
39761
+ const getUserAnalytics = react.useCallback(async (request = {}) => {
39762
+ if (!isInitialized || !sdk) {
39763
+ throw new Error('SDK not initialized. Call initialize() first.');
39764
+ }
39765
+ try {
39766
+ const result = await sdk.analytics.getUserAnalytics(request);
39767
+ return result;
39768
+ }
39769
+ catch (error) {
39770
+ console.error('Failed to fetch user analytics:', error);
39771
+ throw error;
39772
+ }
39773
+ }, [sdk, isInitialized]);
39774
+ /**
39775
+ * Retrieves user transaction ranking with enriched user data
39776
+ *
39777
+ * Returns ranked list of users with full user details (email, externalUserId)
39778
+ * and transaction metrics. Ideal for leaderboards, engagement analysis,
39779
+ * and identifying power users.
39780
+ *
39781
+ * @param request - Ranking request with filters, sorting, and limit
39782
+ * @returns Promise resolving to ranked user list with transaction metrics
39783
+ *
39784
+ * @example Top 50 users by transaction count
39785
+ * ```typescript
39786
+ * const ranking = await getUserRanking({
39787
+ * sortBy: 'totalTransactions',
39788
+ * sortOrder: 'DESC',
39789
+ * limit: 50
39790
+ * });
39791
+ *
39792
+ * ranking.results.forEach((user, index) => {
39793
+ * console.log(`#${index + 1}: ${user.email || user.externalUserId}`);
39794
+ * console.log(` Transactions: ${user.totalTransactions}`);
39795
+ * console.log(` Token spent: ${user.tokenSpent}`);
39796
+ * });
39797
+ * ```
39798
+ *
39799
+ * @example Top STAMP spenders
39800
+ * ```typescript
39801
+ * const ranking = await getUserRanking({
39802
+ * filters: { tokenType: 'STAMP' },
39803
+ * sortBy: 'tokenSpent',
39804
+ * sortOrder: 'DESC',
39805
+ * limit: 20
39806
+ * });
39807
+ * ```
39808
+ */
39809
+ const getUserRanking = react.useCallback(async (request = {}) => {
39810
+ if (!isInitialized || !sdk) {
39811
+ throw new Error('SDK not initialized. Call initialize() first.');
39812
+ }
39813
+ try {
39814
+ const result = await sdk.analytics.getUserRanking(request);
39815
+ return result;
39816
+ }
39817
+ catch (error) {
39818
+ console.error('Failed to fetch user ranking:', error);
39819
+ throw error;
39820
+ }
39821
+ }, [sdk, isInitialized]);
39822
+ /**
39823
+ * Retrieves business transaction ranking with enriched business data
39824
+ *
39825
+ * Returns ranked list of businesses with transaction metrics for
39826
+ * partner analytics and performance dashboards.
39827
+ *
39828
+ * @param request - Ranking request with filters, sorting, and limit
39829
+ * @returns Promise resolving to ranked business list
39830
+ *
39831
+ * @example Top businesses by transaction count
39832
+ * ```typescript
39833
+ * const ranking = await getBusinessRanking({
39834
+ * sortBy: 'totalTransactions',
39835
+ * sortOrder: 'DESC',
39836
+ * limit: 20
39837
+ * });
39838
+ *
39839
+ * ranking.results.forEach((business, index) => {
39840
+ * console.log(`#${index + 1}: ${business.businessId}`);
39841
+ * console.log(` Transactions: ${business.totalTransactions}`);
39842
+ * console.log(` Token spent: ${business.tokenSpent}`);
39843
+ * });
39844
+ * ```
39845
+ */
39846
+ const getBusinessRanking = react.useCallback(async (request = {}) => {
39847
+ if (!isInitialized || !sdk) {
39848
+ throw new Error('SDK not initialized. Call initialize() first.');
39849
+ }
39850
+ try {
39851
+ const result = await sdk.analytics.getBusinessRanking(request);
39852
+ return result;
39853
+ }
39854
+ catch (error) {
39855
+ console.error('Failed to fetch business ranking:', error);
39856
+ throw error;
39857
+ }
39858
+ }, [sdk, isInitialized]);
39859
+ /**
39860
+ * Retrieves monthly user retention analytics
39861
+ *
39862
+ * Returns monthly retention data with active, new, and returning users
39863
+ * along with retention rates. Useful for churn analysis and engagement trends.
39864
+ *
39865
+ * @param request - Retention request with monthsBack and filters
39866
+ * @returns Promise resolving to monthly retention data
39867
+ *
39868
+ * @example Get 12 months of retention data
39869
+ * ```typescript
39870
+ * const retention = await getRetentionAnalytics({
39871
+ * monthsBack: 12
39872
+ * });
39873
+ *
39874
+ * retention.results.forEach(month => {
39875
+ * console.log(`${month.month}:`);
39876
+ * console.log(` Active: ${month.activeUsers}`);
39877
+ * console.log(` New: ${month.newUsers}`);
39878
+ * console.log(` Returning: ${month.returningUsers}`);
39879
+ * console.log(` Retention Rate: ${month.retentionRate.toFixed(1)}%`);
39880
+ * });
39881
+ * ```
39882
+ */
39883
+ const getRetentionAnalytics = react.useCallback(async (request = {}) => {
39884
+ if (!isInitialized || !sdk) {
39885
+ throw new Error('SDK not initialized. Call initialize() first.');
39886
+ }
39887
+ try {
39888
+ const result = await sdk.analytics.getRetentionAnalytics(request);
39889
+ return result;
39890
+ }
39891
+ catch (error) {
39892
+ console.error('Failed to fetch retention analytics:', error);
39893
+ throw error;
39894
+ }
39895
+ }, [sdk, isInitialized]);
37941
39896
  return {
37942
39897
  getTransactionAnalytics,
39898
+ getCampaignClaimAnalytics,
39899
+ getUserAnalytics,
39900
+ getUserRanking,
39901
+ getBusinessRanking,
39902
+ getRetentionAnalytics,
37943
39903
  isAvailable: isInitialized && !!sdk?.analytics,
37944
39904
  };
37945
39905
  };
@@ -38315,6 +40275,277 @@ const useEvents = () => {
38315
40275
  };
38316
40276
  };
38317
40277
 
40278
+ /**
40279
+ * React hook for TriggerSource operations in the PERS SDK
40280
+ *
40281
+ * Manages trigger sources which are physical or digital activation points for campaigns:
40282
+ * - **QR_CODE**: Scannable QR codes at physical locations
40283
+ * - **NFC_TAG**: NFC tap points for contactless interactions
40284
+ * - **GPS_GEOFENCE**: Location-based triggers with radius detection
40285
+ * - **API_WEBHOOK**: External system integration triggers
40286
+ * - **TRANSACTION**: Purchase/payment based triggers
40287
+ *
40288
+ * TriggerSources are standalone entities that can be created, managed, and then
40289
+ * assigned to campaigns. This separation allows reuse across multiple campaigns.
40290
+ *
40291
+ * **Admin Only**: All create, update, and delete operations require admin authentication.
40292
+ *
40293
+ * @returns TriggerSource hook with CRUD operations
40294
+ *
40295
+ * @example Basic TriggerSource Operations
40296
+ * ```typescript
40297
+ * function TriggerSourceManager() {
40298
+ * const {
40299
+ * getAll,
40300
+ * getById,
40301
+ * create,
40302
+ * update,
40303
+ * remove
40304
+ * } = useTriggerSources();
40305
+ *
40306
+ * // List all QR code trigger sources
40307
+ * const loadQRSources = async () => {
40308
+ * const { data: sources } = await getAll({ type: 'QR_CODE' });
40309
+ * console.log('QR Sources:', sources);
40310
+ * };
40311
+ *
40312
+ * // Create a new QR code for a store
40313
+ * const createStoreQR = async () => {
40314
+ * const source = await create({
40315
+ * type: 'QR_CODE',
40316
+ * name: 'Store Entrance QR',
40317
+ * description: 'Scan at the entrance',
40318
+ * businessId: 'business-123',
40319
+ * coordsLatitude: 47.6062,
40320
+ * coordsLongitude: -122.3321
40321
+ * });
40322
+ * console.log('Created:', source.id);
40323
+ * };
40324
+ * }
40325
+ * ```
40326
+ *
40327
+ * @example GPS Geofence Trigger
40328
+ * ```typescript
40329
+ * const { create } = useTriggerSources();
40330
+ *
40331
+ * // Create a GPS geofence around a location
40332
+ * const geofence = await create({
40333
+ * type: 'GPS_GEOFENCE',
40334
+ * name: 'Downtown Area',
40335
+ * coordsLatitude: 47.6062,
40336
+ * coordsLongitude: -122.3321,
40337
+ * metadata: { radiusMeters: 100 }
40338
+ * });
40339
+ * ```
40340
+ */
40341
+ const useTriggerSources = () => {
40342
+ const { sdk, isInitialized, isAuthenticated } = usePersSDK();
40343
+ /**
40344
+ * Get trigger sources with optional filters and pagination
40345
+ *
40346
+ * Retrieves trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.)
40347
+ * that can be used to activate campaigns. Supports filtering by type, business,
40348
+ * campaign association, and active status.
40349
+ *
40350
+ * @param options - Filter and pagination options
40351
+ * @returns Promise resolving to paginated trigger sources
40352
+ *
40353
+ * @example
40354
+ * ```typescript
40355
+ * // Get all QR code trigger sources
40356
+ * const { data: qrSources } = await getAll({ type: 'QR_CODE' });
40357
+ *
40358
+ * // Get trigger sources for a specific business
40359
+ * const { data: businessSources, pagination } = await getAll({
40360
+ * businessId: 'business-123',
40361
+ * active: true,
40362
+ * page: 1,
40363
+ * limit: 20
40364
+ * });
40365
+ *
40366
+ * // Get trigger sources assigned to a campaign
40367
+ * const { data: campaignSources } = await getAll({
40368
+ * campaignId: 'campaign-456'
40369
+ * });
40370
+ * ```
40371
+ */
40372
+ const getAll = react.useCallback(async (options) => {
40373
+ if (!isInitialized || !sdk) {
40374
+ throw new Error('SDK not initialized. Call initialize() first.');
40375
+ }
40376
+ try {
40377
+ const result = await sdk.triggerSources.getAll(options);
40378
+ return result;
40379
+ }
40380
+ catch (error) {
40381
+ console.error('Failed to fetch trigger sources:', error);
40382
+ throw error;
40383
+ }
40384
+ }, [sdk, isInitialized]);
40385
+ /**
40386
+ * Get trigger source by ID
40387
+ *
40388
+ * Retrieves detailed information for a specific trigger source including
40389
+ * its type, location, metadata, and configuration.
40390
+ *
40391
+ * @param triggerSourceId - UUID of the trigger source
40392
+ * @returns Promise resolving to trigger source details
40393
+ * @throws {PersApiError} When trigger source with specified ID is not found
40394
+ *
40395
+ * @example
40396
+ * ```typescript
40397
+ * const source = await getById('source-123');
40398
+ *
40399
+ * console.log('Trigger Source:', source.name);
40400
+ * console.log('Type:', source.type);
40401
+ * console.log('Active:', source.isActive);
40402
+ *
40403
+ * if (source.coordsLatitude && source.coordsLongitude) {
40404
+ * console.log('Location:', source.coordsLatitude, source.coordsLongitude);
40405
+ * }
40406
+ * ```
40407
+ */
40408
+ const getById = react.useCallback(async (triggerSourceId) => {
40409
+ if (!isInitialized || !sdk) {
40410
+ throw new Error('SDK not initialized. Call initialize() first.');
40411
+ }
40412
+ try {
40413
+ const result = await sdk.triggerSources.getById(triggerSourceId);
40414
+ return result;
40415
+ }
40416
+ catch (error) {
40417
+ console.error('Failed to fetch trigger source:', error);
40418
+ throw error;
40419
+ }
40420
+ }, [sdk, isInitialized]);
40421
+ /**
40422
+ * Admin: Create a new trigger source
40423
+ *
40424
+ * Creates a new trigger source (QR code, NFC tag, GPS geofence, webhook, or
40425
+ * transaction-based trigger) that can be assigned to campaigns. Requires
40426
+ * administrator privileges.
40427
+ *
40428
+ * @param triggerSource - Trigger source creation data
40429
+ * @returns Promise resolving to created trigger source
40430
+ * @throws {PersApiError} When not authenticated as admin or validation fails
40431
+ *
40432
+ * @example
40433
+ * ```typescript
40434
+ * // Create a QR code trigger source
40435
+ * const qrSource = await create({
40436
+ * type: 'QR_CODE',
40437
+ * name: 'Store Entrance QR',
40438
+ * description: 'QR code at main entrance',
40439
+ * businessId: 'business-123',
40440
+ * coordsLatitude: 47.6062,
40441
+ * coordsLongitude: -122.3321
40442
+ * });
40443
+ *
40444
+ * // Create an NFC tag trigger source
40445
+ * const nfcSource = await create({
40446
+ * type: 'NFC_TAG',
40447
+ * name: 'Product Display NFC',
40448
+ * metadata: { productId: 'SKU-12345' }
40449
+ * });
40450
+ * ```
40451
+ */
40452
+ const create = react.useCallback(async (triggerSource) => {
40453
+ if (!isInitialized || !sdk) {
40454
+ throw new Error('SDK not initialized. Call initialize() first.');
40455
+ }
40456
+ if (!isAuthenticated) {
40457
+ throw new Error('SDK not authenticated. create requires admin authentication.');
40458
+ }
40459
+ try {
40460
+ const result = await sdk.triggerSources.create(triggerSource);
40461
+ return result;
40462
+ }
40463
+ catch (error) {
40464
+ console.error('Failed to create trigger source:', error);
40465
+ throw error;
40466
+ }
40467
+ }, [sdk, isInitialized, isAuthenticated]);
40468
+ /**
40469
+ * Admin: Update a trigger source
40470
+ *
40471
+ * Updates an existing trigger source's configuration. All fields are optional;
40472
+ * only provided fields will be updated. Requires administrator privileges.
40473
+ *
40474
+ * @param triggerSourceId - UUID of the trigger source to update
40475
+ * @param triggerSource - Updated trigger source data (partial)
40476
+ * @returns Promise resolving to updated trigger source
40477
+ * @throws {PersApiError} When not authenticated as admin or trigger source not found
40478
+ *
40479
+ * @example
40480
+ * ```typescript
40481
+ * // Update trigger source name and location
40482
+ * const updated = await update('source-123', {
40483
+ * name: 'Updated Store Entrance QR',
40484
+ * description: 'New description',
40485
+ * coordsLatitude: 47.6063,
40486
+ * coordsLongitude: -122.3322
40487
+ * });
40488
+ * ```
40489
+ */
40490
+ const update = react.useCallback(async (triggerSourceId, triggerSource) => {
40491
+ if (!isInitialized || !sdk) {
40492
+ throw new Error('SDK not initialized. Call initialize() first.');
40493
+ }
40494
+ if (!isAuthenticated) {
40495
+ throw new Error('SDK not authenticated. update requires admin authentication.');
40496
+ }
40497
+ try {
40498
+ const result = await sdk.triggerSources.update(triggerSourceId, triggerSource);
40499
+ return result;
40500
+ }
40501
+ catch (error) {
40502
+ console.error('Failed to update trigger source:', error);
40503
+ throw error;
40504
+ }
40505
+ }, [sdk, isInitialized, isAuthenticated]);
40506
+ /**
40507
+ * Admin: Delete a trigger source
40508
+ *
40509
+ * Soft deletes a trigger source, making it inactive. The trigger source will
40510
+ * be removed from any campaigns it was assigned to. Requires administrator
40511
+ * privileges.
40512
+ *
40513
+ * @param triggerSourceId - UUID of the trigger source to delete
40514
+ * @returns Promise resolving to success status
40515
+ * @throws {PersApiError} When not authenticated as admin or trigger source not found
40516
+ *
40517
+ * @example
40518
+ * ```typescript
40519
+ * const success = await remove('source-123');
40520
+ * console.log('Trigger source deleted:', success);
40521
+ * ```
40522
+ */
40523
+ const remove = react.useCallback(async (triggerSourceId) => {
40524
+ if (!isInitialized || !sdk) {
40525
+ throw new Error('SDK not initialized. Call initialize() first.');
40526
+ }
40527
+ if (!isAuthenticated) {
40528
+ throw new Error('SDK not authenticated. remove requires admin authentication.');
40529
+ }
40530
+ try {
40531
+ const result = await sdk.triggerSources.delete(triggerSourceId);
40532
+ return result;
40533
+ }
40534
+ catch (error) {
40535
+ console.error('Failed to delete trigger source:', error);
40536
+ throw error;
40537
+ }
40538
+ }, [sdk, isInitialized, isAuthenticated]);
40539
+ return {
40540
+ getAll,
40541
+ getById,
40542
+ create,
40543
+ update,
40544
+ remove,
40545
+ isAvailable: isInitialized && !!sdk?.triggerSources,
40546
+ };
40547
+ };
40548
+
38318
40549
  // ==========================================
38319
40550
  // TOKEN UTILITY FUNCTIONS
38320
40551
  // ==========================================
@@ -38396,12 +40627,22 @@ exports.MEMBERSHIP_ROLE_HIERARCHY = MEMBERSHIP_ROLE_HIERARCHY;
38396
40627
  exports.NativeTokenTypes = NativeTokenTypes;
38397
40628
  exports.PersApiError = PersApiError;
38398
40629
  exports.PersSDKProvider = PersSDKProvider;
40630
+ exports.QUERY_OPERATOR_SQL_MAP = QUERY_OPERATOR_SQL_MAP;
38399
40631
  exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
38400
40632
  exports.ReactNativeHttpClient = ReactNativeHttpClient;
38401
40633
  exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
40634
+ exports.SOURCE_LOGIC_TYPES = SOURCE_LOGIC_TYPES;
40635
+ exports.SOURCE_LOGIC_TYPE_VALUES = SOURCE_LOGIC_TYPE_VALUES;
38402
40636
  exports.SigningStatus = SigningStatus;
38403
40637
  exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
38404
40638
  exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
40639
+ exports.TRIGGER_SOURCE_TYPES = TRIGGER_SOURCE_TYPES;
40640
+ exports.TRIGGER_SOURCE_TYPE_VALUES = TRIGGER_SOURCE_TYPE_VALUES;
40641
+ exports.VALID_CAMPAIGN_CLAIM_RELATIONS = VALID_CAMPAIGN_CLAIM_RELATIONS;
40642
+ exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
40643
+ exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
40644
+ exports.VALID_RELATIONS = VALID_RELATIONS;
40645
+ exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
38405
40646
  exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;
38406
40647
  exports.buildBurnRequest = buildBurnRequest;
38407
40648
  exports.buildMintRequest = buildMintRequest;
@@ -38423,6 +40664,11 @@ exports.hasMinimumRole = hasMinimumRole;
38423
40664
  exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
38424
40665
  exports.isPaginatedResponse = isPaginatedResponse;
38425
40666
  exports.isUserIdentifierObject = isUserIdentifierObject;
40667
+ exports.isValidCampaignClaimRelation = isValidCampaignClaimRelation;
40668
+ exports.isValidCampaignRelation = isValidCampaignRelation;
40669
+ exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
40670
+ exports.isValidRelation = isValidRelation;
40671
+ exports.isValidTransactionRelation = isValidTransactionRelation;
38426
40672
  exports.normalizeToPaginated = normalizeToPaginated;
38427
40673
  exports.registerIdentifierType = registerIdentifierType;
38428
40674
  exports.testnetPrefix = testnetPrefix;
@@ -38443,6 +40689,7 @@ exports.useTokenBalances = useTokenBalances;
38443
40689
  exports.useTokens = useTokens;
38444
40690
  exports.useTransactionSigner = useTransactionSigner;
38445
40691
  exports.useTransactions = useTransactions;
40692
+ exports.useTriggerSources = useTriggerSources;
38446
40693
  exports.useUserStatus = useUserStatus;
38447
40694
  exports.useUsers = useUsers;
38448
40695
  exports.useWeb3 = useWeb3;