@explorins/pers-sdk-react-native 2.3.5 → 2.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4317,14 +4317,15 @@ function computeBookingStatus(checkInDate, checkOutDate, referenceDate = new Dat
4317
4317
  *
4318
4318
  * ## Example Scenarios
4319
4319
  *
4320
- * | Scenario | channel | medium | campaign | source |
4321
- * |-----------------------------|-----------|------------|--------------|--------------|
4322
- * | User clicks email link | `web` | `email` | `summer_promo`| `mailchimp` |
4323
- * | Mobile app registration | `mobile` | `app` | - | - |
4324
- * | Partner API integration | `api` | `affiliate`| - | `partner_xyz`|
4325
- * | QR code scan in hotel | `mobile` | `referral` | `lobby_qr` | `qr_code` |
4326
- * | Admin creates user | `admin` | - | - | - |
4327
- * | Webhook from Stripe | `webhook` | - | - | `stripe` |
4320
+ * | Scenario | channel | platform.deviceType | platform.app/browser | Notes |
4321
+ * |-----------------------------|-----------|---------------------|----------------------|-------|
4322
+ * | Desktop browser | `web` | `desktop` | `browser: Chrome` | - |
4323
+ * | Mobile browser (Safari) | `web` | `phone` | `browser: Safari` | NOT 'mobile' channel |
4324
+ * | Native iOS app | `mobile` | `phone` | `app: MyApp` | Requires x-source-channel |
4325
+ * | Native Android app | `mobile` | `phone` | `app: MyApp` | Requires x-source-channel |
4326
+ * | Partner API integration | `api` | - | - | - |
4327
+ * | Admin creates user | `admin` | - | - | - |
4328
+ * | Webhook from Stripe | `webhook` | - | - | - |
4328
4329
  *
4329
4330
  * @example
4330
4331
  * // Simple usage - just channel
@@ -4341,10 +4342,14 @@ function computeBookingStatus(checkInDate, checkOutDate, referenceDate = new Dat
4341
4342
  */
4342
4343
  /**
4343
4344
  * Primary ingestion channels for data source tracking.
4345
+ *
4346
+ * IMPORTANT:
4347
+ * - 'web' = ANY browser (desktop OR mobile) - use platform.deviceType to distinguish
4348
+ * - 'mobile' = Native apps ONLY (iOS/Android) - must include platform.app
4344
4349
  */
4345
4350
  const DATA_SOURCE_CHANNELS = [
4346
- 'web', // Web application/browser
4347
- 'mobile', // Mobile app (iOS/Android)
4351
+ 'web', // Web browser (desktop or mobile) - check platform.deviceType
4352
+ 'mobile', // Native mobile app ONLY (iOS/Android) - requires platform.app
4348
4353
  'api', // Direct API integration
4349
4354
  'admin', // Admin panel/back-office
4350
4355
  'system', // System-generated (automated)
@@ -4472,17 +4477,114 @@ function detectSourceFromReferrer(referrer) {
4472
4477
  return undefined;
4473
4478
  }
4474
4479
  }
4480
+ /**
4481
+ * Parse User-Agent string for platform information (fallback when SDK doesn't provide).
4482
+ *
4483
+ * Extracts OS, browser, device type from standard User-Agent patterns.
4484
+ * This is best-effort - SDK headers are authoritative.
4485
+ *
4486
+ * @param userAgent - User-Agent header value
4487
+ * @returns Platform object with detected fields, or undefined if unparseable
4488
+ *
4489
+ * @example
4490
+ * parseUserAgentForPlatform('Mozilla/5.0 (iPhone; CPU iPhone OS 17_4...')
4491
+ * // { os: 'iOS', osVersion: '17.4', browser: 'Safari', deviceType: 'phone' }
4492
+ */
4493
+ function parseUserAgentForPlatform(userAgent) {
4494
+ if (!userAgent)
4495
+ return undefined;
4496
+ const ua = userAgent;
4497
+ const platform = {};
4498
+ // Detect OS and device type
4499
+ if (/iPhone|iPod/i.test(ua)) {
4500
+ platform.os = 'iOS';
4501
+ const match = ua.match(/OS (\d+)[_\.](\d+)/);
4502
+ if (match)
4503
+ platform.osVersion = `${match[1]}.${match[2]}`;
4504
+ platform.deviceType = 'phone';
4505
+ }
4506
+ else if (/iPad/i.test(ua)) {
4507
+ platform.os = 'iOS';
4508
+ const match = ua.match(/OS (\d+)[_\.](\d+)/);
4509
+ if (match)
4510
+ platform.osVersion = `${match[1]}.${match[2]}`;
4511
+ platform.deviceType = 'tablet';
4512
+ }
4513
+ else if (/Android/i.test(ua)) {
4514
+ platform.os = 'Android';
4515
+ const match = ua.match(/Android ([\d\.]+)/);
4516
+ if (match)
4517
+ platform.osVersion = match[1];
4518
+ platform.deviceType = /Mobile/i.test(ua) ? 'phone' : 'tablet';
4519
+ }
4520
+ else if (/Windows NT/i.test(ua)) {
4521
+ platform.os = 'Windows';
4522
+ const match = ua.match(/Windows NT ([\d\.]+)/);
4523
+ if (match)
4524
+ platform.osVersion = match[1];
4525
+ platform.deviceType = 'desktop';
4526
+ }
4527
+ else if (/Macintosh|Mac OS X/i.test(ua)) {
4528
+ platform.os = 'macOS';
4529
+ const match = ua.match(/Mac OS X ([\d_]+)/);
4530
+ if (match)
4531
+ platform.osVersion = match[1].replace(/_/g, '.');
4532
+ platform.deviceType = 'desktop';
4533
+ }
4534
+ else if (/Linux/i.test(ua) && !/Android/i.test(ua)) {
4535
+ platform.os = 'Linux';
4536
+ platform.deviceType = 'desktop';
4537
+ }
4538
+ // Detect browser (web channel)
4539
+ if (/Edg\//i.test(ua)) {
4540
+ platform.browser = 'Edge';
4541
+ const match = ua.match(/Edg\/([\d\.]+)/);
4542
+ if (match)
4543
+ platform.browserVersion = match[1];
4544
+ }
4545
+ else if (/Chrome\//i.test(ua) && !/Edg/i.test(ua)) {
4546
+ platform.browser = 'Chrome';
4547
+ const match = ua.match(/Chrome\/([\d\.]+)/);
4548
+ if (match)
4549
+ platform.browserVersion = match[1];
4550
+ }
4551
+ else if (/Safari\//i.test(ua) && !/Chrome/i.test(ua)) {
4552
+ platform.browser = 'Safari';
4553
+ const match = ua.match(/Version\/([\d\.]+)/);
4554
+ if (match)
4555
+ platform.browserVersion = match[1];
4556
+ }
4557
+ else if (/Firefox\//i.test(ua)) {
4558
+ platform.browser = 'Firefox';
4559
+ const match = ua.match(/Firefox\/([\d\.]+)/);
4560
+ if (match)
4561
+ platform.browserVersion = match[1];
4562
+ }
4563
+ else if (/Opera|OPR\//i.test(ua)) {
4564
+ platform.browser = 'Opera';
4565
+ const match = ua.match(/(?:Opera|OPR)\/([\d\.]+)/);
4566
+ if (match)
4567
+ platform.browserVersion = match[1];
4568
+ }
4569
+ return Object.keys(platform).length > 0 ? platform : undefined;
4570
+ }
4475
4571
  /**
4476
4572
  * Auto-detect channel from User-Agent string.
4477
4573
  *
4574
+ * IMPORTANT DESIGN:
4575
+ * - Mobile browsers (Safari on iPhone, Chrome on Android) → 'web' (NOT 'mobile')
4576
+ * - Native apps MUST send explicit x-source-channel: mobile header
4577
+ * - Use platform.deviceType to distinguish mobile web from desktop web
4578
+ *
4478
4579
  * @param userAgent - User-Agent header value
4479
4580
  * @param path - Optional request path for webhook detection
4480
4581
  * @returns Detected channel
4481
4582
  *
4482
4583
  * @example
4483
- * detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...)') // 'mobile'
4484
- * detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web'
4584
+ * detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...) Safari') // 'web' (mobile browser)
4585
+ * detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web' (desktop browser)
4485
4586
  * detectChannelFromUserAgent('', '/webhook/stripe') // 'webhook'
4587
+ * detectChannelFromUserAgent('MyApp/1.0') // 'unknown' (native app without header)
4486
4588
  */
4487
4589
  function detectChannelFromUserAgent(userAgent, path) {
4488
4590
  // Check for webhook paths
@@ -4490,16 +4592,18 @@ function detectChannelFromUserAgent(userAgent, path) {
4490
4592
  return 'webhook';
4491
4593
  }
4492
4594
  const ua = (userAgent || '').toLowerCase();
4493
- // Check for mobile user-agents
4494
- if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(ua)) {
4495
- return 'mobile';
4496
- }
4497
4595
  // Check for bot/crawler patterns (system)
4498
4596
  if (/bot|crawler|spider|scraper|curl|wget|python|node-fetch|axios/i.test(ua)) {
4499
4597
  return 'api';
4500
4598
  }
4501
- // Default to web for browser-like user agents
4502
- return 'web';
4599
+ // Check for ANY browser pattern (desktop OR mobile)
4600
+ // Mobile browsers are 'web' channel - use platform.deviceType to distinguish
4601
+ if (ua && /mozilla|chrome|safari|firefox|edge|opera|msie|trident/i.test(ua)) {
4602
+ return 'web';
4603
+ }
4604
+ // Unknown: Native apps MUST send x-source-channel: mobile header
4605
+ // If we see 'unknown', it means SDK integration is incomplete
4606
+ return 'unknown';
4503
4607
  }
4504
4608
  /**
4505
4609
  * Parse UTM parameters from a URL (deep link, referrer, etc.).
@@ -6347,6 +6451,43 @@ class TokenApi {
6347
6451
  async deleteTokenMetadata(metadataId) {
6348
6452
  return this.apiClient.delete(`${this.basePath}/metadata/${metadataId}`);
6349
6453
  }
6454
+ // ==========================================
6455
+ // 📊 CSV EXPORT
6456
+ // ==========================================
6457
+ /**
6458
+ * ADMIN: Export token metadata to CSV
6459
+ *
6460
+ * Returns a CSV file with all token metadata for the tenant.
6461
+ * Supports date range filtering.
6462
+ *
6463
+ * Endpoint: GET /tokens/metadata/export/csv
6464
+ * Auth: @TenantAdmin()
6465
+ *
6466
+ * @param options - Export options (dateFrom, dateTo)
6467
+ * @returns CSV blob
6468
+ *
6469
+ * @example
6470
+ * ```typescript
6471
+ * // Export all token metadata
6472
+ * const csvBlob = await tokenApi.exportMetadataCSV();
6473
+ *
6474
+ * // Export metadata created in date range
6475
+ * const csvBlob = await tokenApi.exportMetadataCSV({
6476
+ * dateFrom: '2026-01-01',
6477
+ * dateTo: '2026-06-30'
6478
+ * });
6479
+ * ```
6480
+ */
6481
+ async exportMetadataCSV(options) {
6482
+ const params = new URLSearchParams();
6483
+ if (options?.dateFrom)
6484
+ params.append('dateFrom', options.dateFrom);
6485
+ if (options?.dateTo)
6486
+ params.append('dateTo', options.dateTo);
6487
+ const queryString = params.toString();
6488
+ const url = `${this.basePath}/metadata/export/csv${queryString ? `?${queryString}` : ''}`;
6489
+ return this.apiClient.get(url, 'blob');
6490
+ }
6350
6491
  }
6351
6492
 
6352
6493
  /**
@@ -6519,6 +6660,22 @@ class TokenService {
6519
6660
  async deleteTokenMetadata(metadataId) {
6520
6661
  return this.tokenApi.deleteTokenMetadata(metadataId);
6521
6662
  }
6663
+ // ==========================================
6664
+ // 📊 CSV EXPORT
6665
+ // ==========================================
6666
+ /**
6667
+ * ADMIN: Export token metadata to CSV
6668
+ *
6669
+ * Returns a CSV file containing all token metadata for the tenant.
6670
+ * Supports date range filtering.
6671
+ *
6672
+ * @param options - Export options (dateFrom, dateTo)
6673
+ * @returns Promise resolving to CSV blob
6674
+ * @throws {PersApiError} When not authenticated as tenant admin
6675
+ */
6676
+ async exportMetadataCSV(options) {
6677
+ return this.tokenApi.exportMetadataCSV(options);
6678
+ }
6522
6679
  }
6523
6680
 
6524
6681
  /**
@@ -6734,6 +6891,55 @@ class BusinessApi {
6734
6891
  async toggleBusinessStatus(id, dto) {
6735
6892
  return this.apiClient.put(`${this.basePath}/${id}/status`, dto);
6736
6893
  }
6894
+ /**
6895
+ * ADMIN: Generate new business API key (CURRENTLY DISABLED)
6896
+ *
6897
+ * NOTE: This endpoint is commented out in the backend controller.
6898
+ * Uncomment the controller method first before using this.
6899
+ *
6900
+ * Endpoint: PUT /businesses/{id}/api-key
6901
+ * Auth: @TenantAdmin()
6902
+ */
6903
+ // async generateNewBusinessApiKey(id: string): Promise<string> {
6904
+ // return this.apiClient.put<string>(`${this.basePath}/${id}/api-key`, {});
6905
+ // }
6906
+ // ==========================================
6907
+ // 📊 CSV EXPORT
6908
+ // ==========================================
6909
+ /**
6910
+ * ADMIN: Export businesses to CSV
6911
+ *
6912
+ * Returns a CSV file with all business data for the tenant.
6913
+ * Supports date range filtering.
6914
+ *
6915
+ * Endpoint: GET /businesses/export/csv
6916
+ * Auth: @TenantAdmin()
6917
+ *
6918
+ * @param options - Export options (dateFrom, dateTo)
6919
+ * @returns CSV blob
6920
+ *
6921
+ * @example
6922
+ * ```typescript
6923
+ * // Export all businesses
6924
+ * const csvBlob = await businessApi.exportCSV();
6925
+ *
6926
+ * // Export businesses created in date range
6927
+ * const csvBlob = await businessApi.exportCSV({
6928
+ * dateFrom: '2026-01-01',
6929
+ * dateTo: '2026-06-30'
6930
+ * });
6931
+ * ```
6932
+ */
6933
+ async exportCSV(options) {
6934
+ const params = new URLSearchParams();
6935
+ if (options?.dateFrom)
6936
+ params.append('dateFrom', options.dateFrom);
6937
+ if (options?.dateTo)
6938
+ params.append('dateTo', options.dateTo);
6939
+ const queryString = params.toString();
6940
+ const url = `${this.basePath}/export/csv${queryString ? `?${queryString}` : ''}`;
6941
+ return this.apiClient.get(url, 'blob');
6942
+ }
6737
6943
  }
6738
6944
 
6739
6945
  /**
@@ -12121,7 +12327,7 @@ class DefaultAuthProvider {
12121
12327
  /** SDK package name */
12122
12328
  const SDK_NAME = "@explorins/pers-sdk";
12123
12329
  /** SDK version - injected from package.json at build time */
12124
- const SDK_VERSION = "2.3.10";
12330
+ const SDK_VERSION = "2.3.12";
12125
12331
  /** Full SDK identifier for headers */
12126
12332
  const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
12127
12333
 
@@ -14427,6 +14633,41 @@ class TokenManager {
14427
14633
  getTokenService() {
14428
14634
  return this.tokenService;
14429
14635
  }
14636
+ // ==========================================
14637
+ // 📊 CSV EXPORT
14638
+ // ==========================================
14639
+ /**
14640
+ * Admin: Export token metadata to CSV
14641
+ *
14642
+ * Returns a CSV file with all token metadata (rewards/stamps) for the tenant.
14643
+ * Supports date range filtering.
14644
+ *
14645
+ * @param options - Export options (dateFrom, dateTo)
14646
+ * @returns Promise resolving to CSV blob
14647
+ * @throws {PersApiError} When not authenticated as tenant admin
14648
+ *
14649
+ * @example
14650
+ * ```typescript
14651
+ * // Export all token metadata
14652
+ * const csvBlob = await sdk.tokens.exportMetadataCSV();
14653
+ *
14654
+ * // Export metadata created in date range
14655
+ * const csvBlob = await sdk.tokens.exportMetadataCSV({
14656
+ * dateFrom: '2026-01-01',
14657
+ * dateTo: '2026-06-30'
14658
+ * });
14659
+ *
14660
+ * // Download the CSV
14661
+ * const url = URL.createObjectURL(csvBlob);
14662
+ * const a = document.createElement('a');
14663
+ * a.href = url;
14664
+ * a.download = 'token-metadata.csv';
14665
+ * a.click();
14666
+ * ```
14667
+ */
14668
+ async exportMetadataCSV(options) {
14669
+ return this.tokenService.exportMetadataCSV(options);
14670
+ }
14430
14671
  }
14431
14672
 
14432
14673
  /**
@@ -15068,6 +15309,41 @@ class BusinessManager {
15068
15309
  getMembershipService() {
15069
15310
  return this.membershipService;
15070
15311
  }
15312
+ // ==========================================
15313
+ // 📊 CSV EXPORT
15314
+ // ==========================================
15315
+ /**
15316
+ * Admin: Export businesses to CSV
15317
+ *
15318
+ * Returns a CSV file with all business data for the tenant.
15319
+ * Supports date range filtering.
15320
+ *
15321
+ * @param options - Export options (dateFrom, dateTo)
15322
+ * @returns Promise resolving to CSV blob
15323
+ * @throws {PersApiError} When not authenticated as tenant admin
15324
+ *
15325
+ * @example
15326
+ * ```typescript
15327
+ * // Export all businesses
15328
+ * const csvBlob = await sdk.business.exportCSV();
15329
+ *
15330
+ * // Export businesses created in date range
15331
+ * const csvBlob = await sdk.business.exportCSV({
15332
+ * dateFrom: '2026-01-01',
15333
+ * dateTo: '2026-06-30'
15334
+ * });
15335
+ *
15336
+ * // Download the CSV
15337
+ * const url = URL.createObjectURL(csvBlob);
15338
+ * const a = document.createElement('a');
15339
+ * a.href = url;
15340
+ * a.download = 'businesses.csv';
15341
+ * a.click();
15342
+ * ```
15343
+ */
15344
+ async exportCSV(options) {
15345
+ return this.businessApi.exportCSV(options);
15346
+ }
15071
15347
  }
15072
15348
 
15073
15349
  /**
@@ -50749,6 +51025,7 @@ exports.needsSubmission = needsSubmission;
50749
51025
  exports.normalizeFilters = normalizeFilters;
50750
51026
  exports.normalizeToPaginated = normalizeToPaginated;
50751
51027
  exports.operatorText = operatorText;
51028
+ exports.parseUserAgentForPlatform = parseUserAgentForPlatform;
50752
51029
  exports.parseUtmParams = parseUtmParams;
50753
51030
  exports.registerIdentifierType = registerIdentifierType;
50754
51031
  exports.selectOptionsSourceEntities = selectOptionsSourceEntities;