@datalyr/react-native 1.4.9 → 1.6.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 (38) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +17 -127
  3. package/android/build.gradle +0 -7
  4. package/android/src/main/java/com/datalyr/reactnative/DatalyrNativeModule.java +2 -380
  5. package/android/src/main/java/com/datalyr/reactnative/DatalyrPackage.java +1 -1
  6. package/datalyr-react-native.podspec +3 -7
  7. package/expo-module.config.json +4 -1
  8. package/ios/DatalyrNativeModule.swift +0 -266
  9. package/lib/datalyr-sdk.d.ts +8 -4
  10. package/lib/datalyr-sdk.js +83 -143
  11. package/lib/http-client.js +2 -2
  12. package/lib/index.d.ts +1 -1
  13. package/lib/index.js +1 -1
  14. package/lib/integrations/index.d.ts +3 -4
  15. package/lib/integrations/index.js +3 -4
  16. package/lib/native/DatalyrNativeBridge.d.ts +6 -22
  17. package/lib/native/DatalyrNativeBridge.js +6 -147
  18. package/lib/native/index.d.ts +1 -1
  19. package/lib/native/index.js +1 -1
  20. package/lib/types.d.ts +1 -19
  21. package/package.json +3 -5
  22. package/src/datalyr-sdk-expo.ts +6 -141
  23. package/src/datalyr-sdk.ts +96 -173
  24. package/src/http-client.ts +2 -2
  25. package/src/index.ts +1 -1
  26. package/src/integrations/index.ts +3 -4
  27. package/src/native/DatalyrNativeBridge.ts +6 -241
  28. package/src/native/index.ts +0 -2
  29. package/src/types.ts +2 -25
  30. package/src/utils-expo.ts +2 -2
  31. package/ios/DatalyrObjCExceptionCatcher.h +0 -14
  32. package/ios/DatalyrObjCExceptionCatcher.m +0 -30
  33. package/lib/integrations/meta-integration.d.ts +0 -77
  34. package/lib/integrations/meta-integration.js +0 -219
  35. package/lib/integrations/tiktok-integration.d.ts +0 -83
  36. package/lib/integrations/tiktok-integration.js +0 -360
  37. package/src/integrations/meta-integration.ts +0 -239
  38. package/src/integrations/tiktok-integration.ts +0 -363
@@ -31,7 +31,7 @@ import { journeyManager } from './journey';
31
31
  import { createAutoEventsManager, AutoEventsManager, SessionData } from './auto-events';
32
32
  import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
33
33
  import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
34
- import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
34
+ import { appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
35
35
  import { AppleSearchAdsAttribution, AdvertiserInfoBridge } from './native/DatalyrNativeBridge';
36
36
  import { networkStatusManager } from './network-status';
37
37
 
@@ -199,28 +199,6 @@ export class DatalyrSDK {
199
199
  playInstallReferrerIntegration.initialize(),
200
200
  ];
201
201
 
202
- // Add Meta initialization if configured
203
- if (config.meta?.appId) {
204
- platformInitPromises.push(
205
- metaIntegration.initialize(config.meta, config.debug).then(async () => {
206
- // After Meta initializes, fetch deferred deep link
207
- if (config.enableAttribution !== false) {
208
- const deferredLink = await metaIntegration.fetchDeferredDeepLink();
209
- if (deferredLink) {
210
- await this.handleDeferredDeepLink(deferredLink);
211
- }
212
- }
213
- })
214
- );
215
- }
216
-
217
- // Add TikTok initialization if configured
218
- if (config.tiktok?.appId && config.tiktok?.tiktokAppId) {
219
- platformInitPromises.push(
220
- tiktokIntegration.initialize(config.tiktok, config.debug)
221
- );
222
- }
223
-
224
202
  // Wait for all platform integrations to complete
225
203
  await Promise.all(platformInitPromises);
226
204
 
@@ -232,8 +210,6 @@ export class DatalyrSDK {
232
210
  }
233
211
 
234
212
  debugLog('Platform integrations initialized', {
235
- meta: metaIntegration.isAvailable(),
236
- tiktok: tiktokIntegration.isAvailable(),
237
213
  appleSearchAds: appleSearchAdsIntegration.isAvailable(),
238
214
  playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
239
215
  });
@@ -243,10 +219,16 @@ export class DatalyrSDK {
243
219
 
244
220
  // Check for app install (after SDK is marked as initialized)
245
221
  if (attributionManager.isInstall()) {
222
+ // iOS: Attempt deferred web-to-app attribution via IP matching before tracking install
223
+ // Android: Play Store referrer is handled by playInstallReferrerIntegration
224
+ if (Platform.OS === 'ios') {
225
+ await this.fetchDeferredWebAttribution();
226
+ }
227
+
246
228
  const installData = await attributionManager.trackInstall();
247
229
  await this.track('app_install', {
248
230
  platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
249
- sdk_version: '1.4.9',
231
+ sdk_version: '1.6.0',
250
232
  ...installData,
251
233
  });
252
234
  }
@@ -355,31 +337,6 @@ export class DatalyrSDK {
355
337
  const dateOfBirth = (properties?.date_of_birth || properties?.dob || properties?.birthday) as string | undefined;
356
338
  const gender = properties?.gender as string | undefined;
357
339
  const city = properties?.city as string | undefined;
358
- const state = properties?.state as string | undefined;
359
- const zip = (properties?.zip || properties?.postal_code || properties?.zipcode) as string | undefined;
360
- const country = properties?.country as string | undefined;
361
-
362
- // Meta Advanced Matching
363
- if (metaIntegration.isAvailable()) {
364
- metaIntegration.setUserData({
365
- email,
366
- firstName,
367
- lastName,
368
- phone,
369
- dateOfBirth,
370
- gender,
371
- city,
372
- state,
373
- zip,
374
- country,
375
- });
376
- }
377
-
378
- // TikTok identification
379
- if (tiktokIntegration.isAvailable()) {
380
- tiktokIntegration.identify(email, phone, userId);
381
- }
382
-
383
340
  } catch (error) {
384
341
  errorLog('Error identifying user:', error as Error);
385
342
  }
@@ -453,6 +410,88 @@ export class DatalyrSDK {
453
410
  }
454
411
  }
455
412
 
413
+ /**
414
+ * Fetch deferred web attribution on first app install.
415
+ * Uses IP-based matching (iOS) or Play Store referrer (Android) to recover
416
+ * attribution data (fbclid, utm_*, etc.) from a prelander web visit.
417
+ * Called automatically during initialize() when a fresh install is detected.
418
+ */
419
+ private async fetchDeferredWebAttribution(): Promise<void> {
420
+ if (!this.state.config?.apiKey) {
421
+ debugLog('API key not available for deferred attribution fetch');
422
+ return;
423
+ }
424
+
425
+ try {
426
+ debugLog('Fetching deferred web attribution via IP matching...');
427
+
428
+ const baseUrl = this.state.config.endpoint || 'https://api.datalyr.com';
429
+ const controller = new AbortController();
430
+ const timeout = setTimeout(() => controller.abort(), 10000);
431
+
432
+ const response = await fetch(`${baseUrl}/attribution/deferred-lookup`, {
433
+ method: 'POST',
434
+ headers: {
435
+ 'Content-Type': 'application/json',
436
+ 'X-Datalyr-API-Key': this.state.config.apiKey,
437
+ },
438
+ body: JSON.stringify({ platform: Platform.OS }),
439
+ signal: controller.signal,
440
+ });
441
+
442
+ clearTimeout(timeout);
443
+
444
+ if (!response.ok) {
445
+ debugLog('Deferred attribution lookup failed:', response.status);
446
+ return;
447
+ }
448
+
449
+ const result = await response.json() as { found: boolean; attribution?: any };
450
+
451
+ if (!result.found || !result.attribution) {
452
+ debugLog('No deferred web attribution found for this IP');
453
+ return;
454
+ }
455
+
456
+ const webAttribution = result.attribution;
457
+ debugLog('Deferred web attribution found:', {
458
+ visitor_id: webAttribution.visitor_id,
459
+ has_fbclid: !!webAttribution.fbclid,
460
+ has_gclid: !!webAttribution.gclid,
461
+ utm_source: webAttribution.utm_source,
462
+ });
463
+
464
+ // Merge web attribution into current session
465
+ attributionManager.mergeWebAttribution(webAttribution);
466
+
467
+ // Track match event for analytics
468
+ await this.track('$web_attribution_matched', {
469
+ web_visitor_id: webAttribution.visitor_id,
470
+ web_user_id: webAttribution.user_id,
471
+ fbclid: webAttribution.fbclid,
472
+ gclid: webAttribution.gclid,
473
+ ttclid: webAttribution.ttclid,
474
+ gbraid: webAttribution.gbraid,
475
+ wbraid: webAttribution.wbraid,
476
+ fbp: webAttribution.fbp,
477
+ fbc: webAttribution.fbc,
478
+ utm_source: webAttribution.utm_source,
479
+ utm_medium: webAttribution.utm_medium,
480
+ utm_campaign: webAttribution.utm_campaign,
481
+ utm_content: webAttribution.utm_content,
482
+ utm_term: webAttribution.utm_term,
483
+ web_timestamp: webAttribution.timestamp,
484
+ match_method: 'ip',
485
+ });
486
+
487
+ debugLog('Successfully merged deferred web attribution');
488
+
489
+ } catch (error) {
490
+ errorLog('Error fetching deferred web attribution:', error as Error);
491
+ // Non-blocking - email-based fallback will catch this on identify()
492
+ }
493
+ }
494
+
456
495
  /**
457
496
  * Alias a user (connect anonymous user to known user)
458
497
  */
@@ -501,11 +540,6 @@ export class DatalyrSDK {
501
540
  // Generate new session
502
541
  this.state.sessionId = await getOrCreateSessionId();
503
542
 
504
- // Clear user data from platform SDKs
505
- if (metaIntegration.isAvailable()) {
506
- metaIntegration.clearUserData();
507
- }
508
-
509
543
  debugLog('User data reset completed');
510
544
 
511
545
  } catch (error) {
@@ -685,16 +719,6 @@ export class DatalyrSDK {
685
719
  if (productId) properties.product_id = productId;
686
720
 
687
721
  await this.trackWithSKAdNetwork('purchase', properties);
688
-
689
- // Forward to Meta if available
690
- if (metaIntegration.isAvailable()) {
691
- metaIntegration.logPurchase(value, currency, { fb_content_id: productId });
692
- }
693
-
694
- // Forward to TikTok if available
695
- if (tiktokIntegration.isAvailable()) {
696
- tiktokIntegration.logPurchase(value, currency, productId, 'product');
697
- }
698
722
  }
699
723
 
700
724
  /**
@@ -709,16 +733,6 @@ export class DatalyrSDK {
709
733
  if (plan) properties.plan = plan;
710
734
 
711
735
  await this.trackWithSKAdNetwork('subscribe', properties);
712
-
713
- // Forward to Meta if available
714
- if (metaIntegration.isAvailable()) {
715
- metaIntegration.logEvent('Subscribe', value, { subscription_plan: plan });
716
- }
717
-
718
- // Forward to TikTok if available
719
- if (tiktokIntegration.isAvailable()) {
720
- tiktokIntegration.logSubscription(value, currency, plan);
721
- }
722
736
  }
723
737
 
724
738
  // MARK: - Standard E-commerce Events
@@ -737,20 +751,6 @@ export class DatalyrSDK {
737
751
  if (productName) properties.product_name = productName;
738
752
 
739
753
  await this.trackWithSKAdNetwork('add_to_cart', properties);
740
-
741
- // Forward to Meta
742
- if (metaIntegration.isAvailable()) {
743
- metaIntegration.logEvent('AddToCart', value, {
744
- currency,
745
- content_ids: productId ? [productId] : undefined,
746
- content_name: productName,
747
- });
748
- }
749
-
750
- // Forward to TikTok
751
- if (tiktokIntegration.isAvailable()) {
752
- tiktokIntegration.logAddToCart(value, currency, productId, 'product');
753
- }
754
754
  }
755
755
 
756
756
  /**
@@ -770,21 +770,6 @@ export class DatalyrSDK {
770
770
  if (currency) properties.currency = currency;
771
771
 
772
772
  await this.track('view_content', properties);
773
-
774
- // Forward to Meta
775
- if (metaIntegration.isAvailable()) {
776
- metaIntegration.logEvent('ViewContent', value, {
777
- content_ids: contentId ? [contentId] : undefined,
778
- content_name: contentName,
779
- content_type: contentType,
780
- currency,
781
- });
782
- }
783
-
784
- // Forward to TikTok
785
- if (tiktokIntegration.isAvailable()) {
786
- tiktokIntegration.logViewContent(contentId, contentName, contentType);
787
- }
788
773
  }
789
774
 
790
775
  /**
@@ -801,20 +786,6 @@ export class DatalyrSDK {
801
786
  if (productIds) properties.product_ids = productIds;
802
787
 
803
788
  await this.trackWithSKAdNetwork('initiate_checkout', properties);
804
-
805
- // Forward to Meta
806
- if (metaIntegration.isAvailable()) {
807
- metaIntegration.logEvent('InitiateCheckout', value, {
808
- currency,
809
- num_items: numItems,
810
- content_ids: productIds,
811
- });
812
- }
813
-
814
- // Forward to TikTok
815
- if (tiktokIntegration.isAvailable()) {
816
- tiktokIntegration.logInitiateCheckout(value, currency, numItems);
817
- }
818
789
  }
819
790
 
820
791
  /**
@@ -825,16 +796,6 @@ export class DatalyrSDK {
825
796
  if (method) properties.method = method;
826
797
 
827
798
  await this.trackWithSKAdNetwork('complete_registration', properties);
828
-
829
- // Forward to Meta
830
- if (metaIntegration.isAvailable()) {
831
- metaIntegration.logEvent('CompleteRegistration', undefined, { registration_method: method });
832
- }
833
-
834
- // Forward to TikTok
835
- if (tiktokIntegration.isAvailable()) {
836
- tiktokIntegration.logCompleteRegistration(method);
837
- }
838
799
  }
839
800
 
840
801
  /**
@@ -845,19 +806,6 @@ export class DatalyrSDK {
845
806
  if (resultIds) properties.result_ids = resultIds;
846
807
 
847
808
  await this.track('search', properties);
848
-
849
- // Forward to Meta
850
- if (metaIntegration.isAvailable()) {
851
- metaIntegration.logEvent('Search', undefined, {
852
- search_string: query,
853
- content_ids: resultIds,
854
- });
855
- }
856
-
857
- // Forward to TikTok
858
- if (tiktokIntegration.isAvailable()) {
859
- tiktokIntegration.logSearch(query);
860
- }
861
809
  }
862
810
 
863
811
  /**
@@ -869,16 +817,6 @@ export class DatalyrSDK {
869
817
  if (currency) properties.currency = currency;
870
818
 
871
819
  await this.trackWithSKAdNetwork('lead', properties);
872
-
873
- // Forward to Meta
874
- if (metaIntegration.isAvailable()) {
875
- metaIntegration.logEvent('Lead', value, { currency });
876
- }
877
-
878
- // Forward to TikTok
879
- if (tiktokIntegration.isAvailable()) {
880
- tiktokIntegration.logLead(value, currency);
881
- }
882
820
  }
883
821
 
884
822
  /**
@@ -886,16 +824,6 @@ export class DatalyrSDK {
886
824
  */
887
825
  async trackAddPaymentInfo(success = true): Promise<void> {
888
826
  await this.track('add_payment_info', { success });
889
-
890
- // Forward to Meta
891
- if (metaIntegration.isAvailable()) {
892
- metaIntegration.logEvent('AddPaymentInfo', undefined, { success: success ? 1 : 0 });
893
- }
894
-
895
- // Forward to TikTok
896
- if (tiktokIntegration.isAvailable()) {
897
- tiktokIntegration.logAddPaymentInfo(success);
898
- }
899
827
  }
900
828
 
901
829
  // MARK: - Platform Integration Methods
@@ -904,16 +832,14 @@ export class DatalyrSDK {
904
832
  * Get deferred attribution data from platform SDKs
905
833
  */
906
834
  getDeferredAttributionData(): DeferredDeepLinkResult | null {
907
- return metaIntegration.getDeferredDeepLinkData();
835
+ return null;
908
836
  }
909
837
 
910
838
  /**
911
839
  * Get platform integration status
912
840
  */
913
- getPlatformIntegrationStatus(): { meta: boolean; tiktok: boolean; appleSearchAds: boolean; playInstallReferrer: boolean } {
841
+ getPlatformIntegrationStatus(): { appleSearchAds: boolean; playInstallReferrer: boolean } {
914
842
  return {
915
- meta: metaIntegration.isAvailable(),
916
- tiktok: tiktokIntegration.isAvailable(),
917
843
  appleSearchAds: appleSearchAdsIntegration.isAvailable(),
918
844
  playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
919
845
  };
@@ -946,9 +872,6 @@ export class DatalyrSDK {
946
872
  return;
947
873
  }
948
874
 
949
- metaIntegration.updateTrackingAuthorization(enabled);
950
- tiktokIntegration.updateTrackingAuthorization(enabled);
951
-
952
875
  // Refresh cached advertiser info after ATT status change
953
876
  try {
954
877
  this.cachedAdvertiserInfo = await AdvertiserInfoBridge.getAdvertiserInfo();
@@ -1068,8 +991,8 @@ export class DatalyrSDK {
1068
991
  carrier: deviceInfo.carrier,
1069
992
  network_type: getNetworkType(),
1070
993
  timestamp: Date.now(),
1071
- sdk_version: '1.4.9',
1072
- // Advertiser data (IDFA/GAID, ATT status) for Meta CAPI / TikTok Events API
994
+ sdk_version: '1.6.0',
995
+ // Advertiser data (IDFA/GAID, ATT status) for server-side postback
1073
996
  ...(advertiserInfo ? {
1074
997
  idfa: advertiserInfo.idfa,
1075
998
  idfv: advertiserInfo.idfv,
@@ -1380,7 +1303,7 @@ export class Datalyr {
1380
1303
  datalyr.updateAutoEventsConfig(config);
1381
1304
  }
1382
1305
 
1383
- // Standard e-commerce events (all forward to Meta and TikTok)
1306
+ // Standard e-commerce events
1384
1307
  static async trackAddToCart(
1385
1308
  value: number,
1386
1309
  currency = 'USD',
@@ -1430,7 +1353,7 @@ export class Datalyr {
1430
1353
  return datalyr.getDeferredAttributionData();
1431
1354
  }
1432
1355
 
1433
- static getPlatformIntegrationStatus(): { meta: boolean; tiktok: boolean; appleSearchAds: boolean } {
1356
+ static getPlatformIntegrationStatus(): { appleSearchAds: boolean; playInstallReferrer: boolean } {
1434
1357
  return datalyr.getPlatformIntegrationStatus();
1435
1358
  }
1436
1359
 
@@ -72,7 +72,7 @@ export class HttpClient {
72
72
 
73
73
  const headers: Record<string, string> = {
74
74
  'Content-Type': 'application/json',
75
- 'User-Agent': `@datalyr/react-native/1.4.8`,
75
+ 'User-Agent': `@datalyr/react-native/1.5.0`,
76
76
  };
77
77
 
78
78
  // Server-side tracking uses X-API-Key header
@@ -197,7 +197,7 @@ export class HttpClient {
197
197
  },
198
198
  context: {
199
199
  library: '@datalyr/react-native',
200
- version: '1.4.8',
200
+ version: '1.5.0',
201
201
  source: 'mobile_app', // Explicitly set source for mobile
202
202
  userProperties: payload.userProperties,
203
203
  },
package/src/index.ts CHANGED
@@ -29,7 +29,7 @@ export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEn
29
29
  export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
30
30
 
31
31
  // Export platform integrations
32
- export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
32
+ export { appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
33
33
 
34
34
  // Export network status manager
35
35
  export { networkStatusManager } from './network-status';
@@ -1,10 +1,9 @@
1
1
  /**
2
- * Platform SDK Integrations
3
- * Meta (Facebook), TikTok, Apple Search Ads, and Google Play Install Referrer SDK wrappers
2
+ * Platform Integrations
3
+ * Apple Search Ads and Google Play Install Referrer
4
+ * Conversion routing to Meta/TikTok/Google is handled server-side via postback.
4
5
  */
5
6
 
6
- export { MetaIntegration, metaIntegration } from './meta-integration';
7
- export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
8
7
  export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
9
8
  export { playInstallReferrerIntegration } from './play-install-referrer';
10
9
  export type { PlayInstallReferrer } from './play-install-referrer';