@datalyr/react-native 1.1.1 → 1.2.0

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 (42) hide show
  1. package/CHANGELOG.md +24 -141
  2. package/LICENSE +21 -0
  3. package/README.md +405 -217
  4. package/datalyr-react-native.podspec +31 -0
  5. package/ios/DatalyrNative.m +70 -0
  6. package/ios/DatalyrNative.swift +275 -0
  7. package/ios/DatalyrSKAdNetwork.m +26 -0
  8. package/lib/datalyr-sdk.d.ts +64 -3
  9. package/lib/datalyr-sdk.js +322 -3
  10. package/lib/index.d.ts +1 -0
  11. package/lib/index.js +4 -2
  12. package/lib/integrations/index.d.ts +6 -0
  13. package/lib/integrations/index.js +6 -0
  14. package/lib/integrations/meta-integration.d.ts +76 -0
  15. package/lib/integrations/meta-integration.js +218 -0
  16. package/lib/integrations/tiktok-integration.d.ts +82 -0
  17. package/lib/integrations/tiktok-integration.js +356 -0
  18. package/lib/native/DatalyrNativeBridge.d.ts +31 -0
  19. package/lib/native/DatalyrNativeBridge.js +168 -0
  20. package/lib/native/index.d.ts +5 -0
  21. package/lib/native/index.js +5 -0
  22. package/lib/types.d.ts +29 -0
  23. package/package.json +10 -5
  24. package/src/datalyr-sdk-expo.ts +957 -0
  25. package/src/datalyr-sdk.ts +419 -19
  26. package/src/expo.ts +38 -18
  27. package/src/index.ts +5 -2
  28. package/src/integrations/index.ts +7 -0
  29. package/src/integrations/meta-integration.ts +238 -0
  30. package/src/integrations/tiktok-integration.ts +360 -0
  31. package/src/native/DatalyrNativeBridge.ts +271 -0
  32. package/src/native/index.ts +11 -0
  33. package/src/types.ts +39 -0
  34. package/src/utils-expo.ts +25 -3
  35. package/src/utils-interface.ts +38 -0
  36. package/EXPO_INSTALL.md +0 -297
  37. package/INSTALL.md +0 -402
  38. package/examples/attribution-example.tsx +0 -377
  39. package/examples/auto-events-example.tsx +0 -403
  40. package/examples/example.tsx +0 -250
  41. package/examples/skadnetwork-example.tsx +0 -380
  42. package/examples/test-implementation.tsx +0 -163
@@ -6,6 +6,7 @@ import { attributionManager } from './attribution';
6
6
  import { AutoEventsManager } from './auto-events';
7
7
  import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
8
8
  import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
9
+ import { metaIntegration, tiktokIntegration } from './integrations';
9
10
  export class DatalyrSDK {
10
11
  constructor() {
11
12
  this.autoEventsManager = null;
@@ -41,7 +42,7 @@ export class DatalyrSDK {
41
42
  * Initialize the SDK with configuration
42
43
  */
43
44
  async initialize(config) {
44
- var _a;
45
+ var _a, _b, _c, _d;
45
46
  try {
46
47
  debugLog('Initializing Datalyr SDK...', { workspaceId: config.workspaceId });
47
48
  // Validate configuration
@@ -116,6 +117,25 @@ export class DatalyrSDK {
116
117
  }
117
118
  }
118
119
  }
120
+ // Initialize Meta SDK if configured
121
+ if ((_b = config.meta) === null || _b === void 0 ? void 0 : _b.appId) {
122
+ await metaIntegration.initialize(config.meta, config.debug);
123
+ // Fetch deferred deep link and merge with attribution
124
+ if (config.enableAttribution !== false) {
125
+ const deferredLink = await metaIntegration.fetchDeferredDeepLink();
126
+ if (deferredLink) {
127
+ await this.handleDeferredDeepLink(deferredLink);
128
+ }
129
+ }
130
+ }
131
+ // Initialize TikTok SDK if configured
132
+ if (((_c = config.tiktok) === null || _c === void 0 ? void 0 : _c.appId) && ((_d = config.tiktok) === null || _d === void 0 ? void 0 : _d.tiktokAppId)) {
133
+ await tiktokIntegration.initialize(config.tiktok, config.debug);
134
+ }
135
+ debugLog('Platform integrations initialized', {
136
+ meta: metaIntegration.isAvailable(),
137
+ tiktok: tiktokIntegration.isAvailable(),
138
+ });
119
139
  // SDK initialized successfully - set state before tracking install event
120
140
  this.state.initialized = true;
121
141
  // Check for app install (after SDK is marked as initialized)
@@ -207,6 +227,36 @@ export class DatalyrSDK {
207
227
  await this.fetchAndMergeWebAttribution(email);
208
228
  }
209
229
  }
230
+ // Forward user data to platform SDKs for Advanced Matching
231
+ const email = properties === null || properties === void 0 ? void 0 : properties.email;
232
+ const phone = properties === null || properties === void 0 ? void 0 : properties.phone;
233
+ const firstName = ((properties === null || properties === void 0 ? void 0 : properties.first_name) || (properties === null || properties === void 0 ? void 0 : properties.firstName));
234
+ const lastName = ((properties === null || properties === void 0 ? void 0 : properties.last_name) || (properties === null || properties === void 0 ? void 0 : properties.lastName));
235
+ const dateOfBirth = ((properties === null || properties === void 0 ? void 0 : properties.date_of_birth) || (properties === null || properties === void 0 ? void 0 : properties.dob) || (properties === null || properties === void 0 ? void 0 : properties.birthday));
236
+ const gender = properties === null || properties === void 0 ? void 0 : properties.gender;
237
+ const city = properties === null || properties === void 0 ? void 0 : properties.city;
238
+ const state = properties === null || properties === void 0 ? void 0 : properties.state;
239
+ const zip = ((properties === null || properties === void 0 ? void 0 : properties.zip) || (properties === null || properties === void 0 ? void 0 : properties.postal_code) || (properties === null || properties === void 0 ? void 0 : properties.zipcode));
240
+ const country = properties === null || properties === void 0 ? void 0 : properties.country;
241
+ // Meta Advanced Matching
242
+ if (metaIntegration.isAvailable()) {
243
+ metaIntegration.setUserData({
244
+ email,
245
+ firstName,
246
+ lastName,
247
+ phone,
248
+ dateOfBirth,
249
+ gender,
250
+ city,
251
+ state,
252
+ zip,
253
+ country,
254
+ });
255
+ }
256
+ // TikTok identification
257
+ if (tiktokIntegration.isAvailable()) {
258
+ tiktokIntegration.identify(email, phone, userId);
259
+ }
210
260
  }
211
261
  catch (error) {
212
262
  errorLog('Error identifying user:', error);
@@ -310,6 +360,10 @@ export class DatalyrSDK {
310
360
  await Storage.removeItem(STORAGE_KEYS.USER_PROPERTIES);
311
361
  // Generate new session
312
362
  this.state.sessionId = await getOrCreateSessionId();
363
+ // Clear user data from platform SDKs
364
+ if (metaIntegration.isAvailable()) {
365
+ metaIntegration.clearUserData();
366
+ }
313
367
  debugLog('User data reset completed');
314
368
  }
315
369
  catch (error) {
@@ -426,22 +480,255 @@ export class DatalyrSDK {
426
480
  }
427
481
  }
428
482
  /**
429
- * Track purchase with automatic revenue encoding
483
+ * Track purchase with automatic revenue encoding and platform forwarding
430
484
  */
431
485
  async trackPurchase(value, currency = 'USD', productId) {
432
486
  const properties = { revenue: value, currency };
433
487
  if (productId)
434
488
  properties.product_id = productId;
435
489
  await this.trackWithSKAdNetwork('purchase', properties);
490
+ // Forward to Meta if available
491
+ if (metaIntegration.isAvailable()) {
492
+ metaIntegration.logPurchase(value, currency, { fb_content_id: productId });
493
+ }
494
+ // Forward to TikTok if available
495
+ if (tiktokIntegration.isAvailable()) {
496
+ tiktokIntegration.logPurchase(value, currency, productId, 'product');
497
+ }
436
498
  }
437
499
  /**
438
- * Track subscription with automatic revenue encoding
500
+ * Track subscription with automatic revenue encoding and platform forwarding
439
501
  */
440
502
  async trackSubscription(value, currency = 'USD', plan) {
441
503
  const properties = { revenue: value, currency };
442
504
  if (plan)
443
505
  properties.plan = plan;
444
506
  await this.trackWithSKAdNetwork('subscribe', properties);
507
+ // Forward to Meta if available
508
+ if (metaIntegration.isAvailable()) {
509
+ metaIntegration.logEvent('Subscribe', value, { subscription_plan: plan });
510
+ }
511
+ // Forward to TikTok if available
512
+ if (tiktokIntegration.isAvailable()) {
513
+ tiktokIntegration.logSubscription(value, currency, plan);
514
+ }
515
+ }
516
+ // MARK: - Standard E-commerce Events
517
+ /**
518
+ * Track add to cart event
519
+ */
520
+ async trackAddToCart(value, currency = 'USD', productId, productName) {
521
+ const properties = { value, currency };
522
+ if (productId)
523
+ properties.product_id = productId;
524
+ if (productName)
525
+ properties.product_name = productName;
526
+ await this.trackWithSKAdNetwork('add_to_cart', properties);
527
+ // Forward to Meta
528
+ if (metaIntegration.isAvailable()) {
529
+ metaIntegration.logEvent('AddToCart', value, {
530
+ currency,
531
+ content_ids: productId ? [productId] : undefined,
532
+ content_name: productName,
533
+ });
534
+ }
535
+ // Forward to TikTok
536
+ if (tiktokIntegration.isAvailable()) {
537
+ tiktokIntegration.logAddToCart(value, currency, productId, 'product');
538
+ }
539
+ }
540
+ /**
541
+ * Track view content/product event
542
+ */
543
+ async trackViewContent(contentId, contentName, contentType = 'product', value, currency) {
544
+ const properties = { content_type: contentType };
545
+ if (contentId)
546
+ properties.content_id = contentId;
547
+ if (contentName)
548
+ properties.content_name = contentName;
549
+ if (value !== undefined)
550
+ properties.value = value;
551
+ if (currency)
552
+ properties.currency = currency;
553
+ await this.track('view_content', properties);
554
+ // Forward to Meta
555
+ if (metaIntegration.isAvailable()) {
556
+ metaIntegration.logEvent('ViewContent', value, {
557
+ content_ids: contentId ? [contentId] : undefined,
558
+ content_name: contentName,
559
+ content_type: contentType,
560
+ currency,
561
+ });
562
+ }
563
+ // Forward to TikTok
564
+ if (tiktokIntegration.isAvailable()) {
565
+ tiktokIntegration.logViewContent(contentId, contentName, contentType);
566
+ }
567
+ }
568
+ /**
569
+ * Track initiate checkout event
570
+ */
571
+ async trackInitiateCheckout(value, currency = 'USD', numItems, productIds) {
572
+ const properties = { value, currency };
573
+ if (numItems !== undefined)
574
+ properties.num_items = numItems;
575
+ if (productIds)
576
+ properties.product_ids = productIds;
577
+ await this.trackWithSKAdNetwork('initiate_checkout', properties);
578
+ // Forward to Meta
579
+ if (metaIntegration.isAvailable()) {
580
+ metaIntegration.logEvent('InitiateCheckout', value, {
581
+ currency,
582
+ num_items: numItems,
583
+ content_ids: productIds,
584
+ });
585
+ }
586
+ // Forward to TikTok
587
+ if (tiktokIntegration.isAvailable()) {
588
+ tiktokIntegration.logInitiateCheckout(value, currency, numItems);
589
+ }
590
+ }
591
+ /**
592
+ * Track complete registration event
593
+ */
594
+ async trackCompleteRegistration(method) {
595
+ const properties = {};
596
+ if (method)
597
+ properties.method = method;
598
+ await this.trackWithSKAdNetwork('complete_registration', properties);
599
+ // Forward to Meta
600
+ if (metaIntegration.isAvailable()) {
601
+ metaIntegration.logEvent('CompleteRegistration', undefined, { registration_method: method });
602
+ }
603
+ // Forward to TikTok
604
+ if (tiktokIntegration.isAvailable()) {
605
+ tiktokIntegration.logCompleteRegistration(method);
606
+ }
607
+ }
608
+ /**
609
+ * Track search event
610
+ */
611
+ async trackSearch(query, resultIds) {
612
+ const properties = { query };
613
+ if (resultIds)
614
+ properties.result_ids = resultIds;
615
+ await this.track('search', properties);
616
+ // Forward to Meta
617
+ if (metaIntegration.isAvailable()) {
618
+ metaIntegration.logEvent('Search', undefined, {
619
+ search_string: query,
620
+ content_ids: resultIds,
621
+ });
622
+ }
623
+ // Forward to TikTok
624
+ if (tiktokIntegration.isAvailable()) {
625
+ tiktokIntegration.logSearch(query);
626
+ }
627
+ }
628
+ /**
629
+ * Track lead/contact form submission
630
+ */
631
+ async trackLead(value, currency) {
632
+ const properties = {};
633
+ if (value !== undefined)
634
+ properties.value = value;
635
+ if (currency)
636
+ properties.currency = currency;
637
+ await this.trackWithSKAdNetwork('lead', properties);
638
+ // Forward to Meta
639
+ if (metaIntegration.isAvailable()) {
640
+ metaIntegration.logEvent('Lead', value, { currency });
641
+ }
642
+ // Forward to TikTok
643
+ if (tiktokIntegration.isAvailable()) {
644
+ tiktokIntegration.logLead(value, currency);
645
+ }
646
+ }
647
+ /**
648
+ * Track add payment info event
649
+ */
650
+ async trackAddPaymentInfo(success = true) {
651
+ await this.track('add_payment_info', { success });
652
+ // Forward to Meta
653
+ if (metaIntegration.isAvailable()) {
654
+ metaIntegration.logEvent('AddPaymentInfo', undefined, { success: success ? 1 : 0 });
655
+ }
656
+ // Forward to TikTok
657
+ if (tiktokIntegration.isAvailable()) {
658
+ tiktokIntegration.logAddPaymentInfo(success);
659
+ }
660
+ }
661
+ // MARK: - Platform Integration Methods
662
+ /**
663
+ * Get deferred attribution data from platform SDKs
664
+ */
665
+ getDeferredAttributionData() {
666
+ return metaIntegration.getDeferredDeepLinkData();
667
+ }
668
+ /**
669
+ * Get platform integration status
670
+ */
671
+ getPlatformIntegrationStatus() {
672
+ return {
673
+ meta: metaIntegration.isAvailable(),
674
+ tiktok: tiktokIntegration.isAvailable(),
675
+ };
676
+ }
677
+ /**
678
+ * Update tracking authorization status on all platform SDKs
679
+ * Call this AFTER the user responds to the ATT permission dialog
680
+ */
681
+ async updateTrackingAuthorization(enabled) {
682
+ if (!this.state.initialized) {
683
+ errorLog('SDK not initialized. Call initialize() first.');
684
+ return;
685
+ }
686
+ metaIntegration.updateTrackingAuthorization(enabled);
687
+ tiktokIntegration.updateTrackingAuthorization(enabled);
688
+ // Track ATT status event
689
+ await this.track('$att_status', {
690
+ authorized: enabled,
691
+ status: enabled ? 3 : 2,
692
+ status_name: enabled ? 'authorized' : 'denied',
693
+ });
694
+ debugLog(`ATT status updated: ${enabled ? 'authorized' : 'denied'}`);
695
+ }
696
+ /**
697
+ * Handle deferred deep link data from platform SDKs
698
+ */
699
+ async handleDeferredDeepLink(data) {
700
+ try {
701
+ debugLog('Processing deferred deep link:', data);
702
+ // Track deferred attribution event
703
+ await this.track('$deferred_deep_link', {
704
+ url: data.url,
705
+ source: data.source,
706
+ fbclid: data.fbclid,
707
+ ttclid: data.ttclid,
708
+ utm_source: data.utmSource,
709
+ utm_medium: data.utmMedium,
710
+ utm_campaign: data.utmCampaign,
711
+ utm_content: data.utmContent,
712
+ utm_term: data.utmTerm,
713
+ campaign_id: data.campaignId,
714
+ adset_id: data.adsetId,
715
+ ad_id: data.adId,
716
+ });
717
+ // Merge into attribution manager
718
+ attributionManager.mergeWebAttribution({
719
+ fbclid: data.fbclid,
720
+ ttclid: data.ttclid,
721
+ utm_source: data.utmSource,
722
+ utm_medium: data.utmMedium,
723
+ utm_campaign: data.utmCampaign,
724
+ utm_content: data.utmContent,
725
+ utm_term: data.utmTerm,
726
+ });
727
+ debugLog('Deferred deep link processed successfully');
728
+ }
729
+ catch (error) {
730
+ errorLog('Error processing deferred deep link:', error);
731
+ }
445
732
  }
446
733
  /**
447
734
  * Get conversion value for testing (doesn't send to Apple)
@@ -683,6 +970,38 @@ export class Datalyr {
683
970
  static updateAutoEventsConfig(config) {
684
971
  datalyr.updateAutoEventsConfig(config);
685
972
  }
973
+ // Standard e-commerce events (all forward to Meta and TikTok)
974
+ static async trackAddToCart(value, currency = 'USD', productId, productName) {
975
+ await datalyr.trackAddToCart(value, currency, productId, productName);
976
+ }
977
+ static async trackViewContent(contentId, contentName, contentType = 'product', value, currency) {
978
+ await datalyr.trackViewContent(contentId, contentName, contentType, value, currency);
979
+ }
980
+ static async trackInitiateCheckout(value, currency = 'USD', numItems, productIds) {
981
+ await datalyr.trackInitiateCheckout(value, currency, numItems, productIds);
982
+ }
983
+ static async trackCompleteRegistration(method) {
984
+ await datalyr.trackCompleteRegistration(method);
985
+ }
986
+ static async trackSearch(query, resultIds) {
987
+ await datalyr.trackSearch(query, resultIds);
988
+ }
989
+ static async trackLead(value, currency) {
990
+ await datalyr.trackLead(value, currency);
991
+ }
992
+ static async trackAddPaymentInfo(success = true) {
993
+ await datalyr.trackAddPaymentInfo(success);
994
+ }
995
+ // Platform integration methods
996
+ static getDeferredAttributionData() {
997
+ return datalyr.getDeferredAttributionData();
998
+ }
999
+ static getPlatformIntegrationStatus() {
1000
+ return datalyr.getPlatformIntegrationStatus();
1001
+ }
1002
+ static async updateTrackingAuthorization(enabled) {
1003
+ await datalyr.updateTrackingAuthorization(enabled);
1004
+ }
686
1005
  }
687
1006
  // Export default instance for backward compatibility
688
1007
  export default datalyr;
package/lib/index.d.ts CHANGED
@@ -10,4 +10,5 @@ export * from './event-queue';
10
10
  export { DatalyrSDK };
11
11
  export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
12
12
  export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
13
+ export { metaIntegration, tiktokIntegration } from './integrations';
13
14
  export default DatalyrSDK;
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Main entry point for React Native CLI
2
- // Import this with: import { datalyr } from '@datalyr/react-native-sdk';
2
+ // Import with: import { Datalyr } from '@datalyr/react-native';
3
3
  import { DatalyrSDK, Datalyr } from './datalyr-sdk';
4
4
  // Create singleton instance for easy usage
5
5
  export const datalyr = new DatalyrSDK();
@@ -18,5 +18,7 @@ export { DatalyrSDK };
18
18
  // Export SKAdNetwork components
19
19
  export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
20
20
  export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
21
- // Default export for compatibility
21
+ // Export platform integrations
22
+ export { metaIntegration, tiktokIntegration } from './integrations';
23
+ // Default export for compatibility
22
24
  export default DatalyrSDK;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Platform SDK Integrations
3
+ * Meta (Facebook) and TikTok SDK wrappers for deferred deep linking and event forwarding
4
+ */
5
+ export { MetaIntegration, metaIntegration } from './meta-integration';
6
+ export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Platform SDK Integrations
3
+ * Meta (Facebook) and TikTok SDK wrappers for deferred deep linking and event forwarding
4
+ */
5
+ export { MetaIntegration, metaIntegration } from './meta-integration';
6
+ export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Meta (Facebook) SDK Integration
3
+ * Uses bundled native iOS SDK for deferred deep linking, event forwarding, and Advanced Matching
4
+ */
5
+ import { MetaConfig, DeferredDeepLinkResult } from '../types';
6
+ /**
7
+ * Meta Integration class for handling Facebook SDK operations
8
+ * Uses native iOS SDK bundled via CocoaPods (no additional npm packages required)
9
+ */
10
+ export declare class MetaIntegration {
11
+ private config;
12
+ private initialized;
13
+ private available;
14
+ private debug;
15
+ private deferredDeepLinkData;
16
+ /**
17
+ * Initialize Meta SDK with configuration
18
+ */
19
+ initialize(config: MetaConfig, debug?: boolean): Promise<void>;
20
+ /**
21
+ * Update tracking authorization status (call after ATT prompt)
22
+ */
23
+ updateTrackingAuthorization(enabled: boolean): Promise<void>;
24
+ /**
25
+ * Fetch deferred deep link from Meta SDK
26
+ * This captures fbclid for installs that went through App Store
27
+ */
28
+ fetchDeferredDeepLink(): Promise<DeferredDeepLinkResult | null>;
29
+ /**
30
+ * Parse deep link URL to extract attribution parameters
31
+ */
32
+ private parseDeepLinkUrl;
33
+ /**
34
+ * Log purchase event to Meta
35
+ */
36
+ logPurchase(value: number, currency: string, parameters?: Record<string, any>): Promise<void>;
37
+ /**
38
+ * Log custom event to Meta
39
+ */
40
+ logEvent(eventName: string, valueToSum?: number, parameters?: Record<string, any>): Promise<void>;
41
+ /**
42
+ * Set user data for Advanced Matching (improves conversion attribution)
43
+ * Note: Meta's Advanced Matching uses these specific fields - externalId is not supported
44
+ */
45
+ setUserData(userData: {
46
+ email?: string;
47
+ firstName?: string;
48
+ lastName?: string;
49
+ phone?: string;
50
+ dateOfBirth?: string;
51
+ gender?: string;
52
+ city?: string;
53
+ state?: string;
54
+ zip?: string;
55
+ country?: string;
56
+ }): Promise<void>;
57
+ /**
58
+ * Clear user data (call on logout)
59
+ */
60
+ clearUserData(): Promise<void>;
61
+ /**
62
+ * Get cached deferred deep link data
63
+ */
64
+ getDeferredDeepLinkData(): DeferredDeepLinkResult | null;
65
+ /**
66
+ * Check if Meta SDK is available and initialized
67
+ */
68
+ isAvailable(): boolean;
69
+ /**
70
+ * Check if Meta SDK native module is installed
71
+ */
72
+ isInstalled(): boolean;
73
+ private log;
74
+ private logError;
75
+ }
76
+ export declare const metaIntegration: MetaIntegration;