@datalyr/react-native 1.4.7 → 1.4.9

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.
@@ -45,7 +45,7 @@ export class HttpClient {
45
45
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
46
46
  const headers = {
47
47
  'Content-Type': 'application/json',
48
- 'User-Agent': `@datalyr/react-native/1.0.2`,
48
+ 'User-Agent': `@datalyr/react-native/1.4.8`,
49
49
  };
50
50
  // Server-side tracking uses X-API-Key header
51
51
  if (this.config.useServerTracking !== false) {
@@ -143,7 +143,7 @@ export class HttpClient {
143
143
  return {
144
144
  event: payload.eventName,
145
145
  userId: payload.userId || payload.visitorId,
146
- anonymousId: payload.visitorId,
146
+ anonymousId: payload.anonymousId || payload.visitorId,
147
147
  properties: {
148
148
  ...payload.eventData,
149
149
  sessionId: payload.sessionId,
@@ -152,7 +152,7 @@ export class HttpClient {
152
152
  },
153
153
  context: {
154
154
  library: '@datalyr/react-native',
155
- version: '1.0.5',
155
+ version: '1.4.8',
156
156
  source: 'mobile_app', // Explicitly set source for mobile
157
157
  userProperties: payload.userProperties,
158
158
  },
@@ -62,9 +62,12 @@ export class TikTokIntegration {
62
62
  this.initialized = true;
63
63
  this.log(`TikTok SDK initialized with App ID: ${config.tiktokAppId}`);
64
64
  }
65
+ else {
66
+ console.warn('[Datalyr/TikTok] TikTok SDK not initialized (accessToken may be missing). Events will still be sent server-side via Datalyr postbacks.');
67
+ }
65
68
  }
66
69
  catch (error) {
67
- this.logError('Failed to initialize TikTok SDK:', error);
70
+ console.warn('[Datalyr/TikTok] TikTok SDK init failed. Events will still be sent server-side via Datalyr postbacks.', error);
68
71
  }
69
72
  }
70
73
  /**
@@ -78,6 +78,21 @@ export declare const AppleSearchAdsNativeBridge: {
78
78
  */
79
79
  getAttribution(): Promise<AppleSearchAdsAttribution | null>;
80
80
  };
81
+ export interface AdvertiserInfo {
82
+ idfa?: string;
83
+ idfv?: string;
84
+ gaid?: string;
85
+ att_status: number;
86
+ advertiser_tracking_enabled: boolean;
87
+ }
88
+ export declare const AdvertiserInfoBridge: {
89
+ /**
90
+ * Get advertiser info (IDFA, IDFV, ATT status)
91
+ * IDFA is only available when ATT is authorized (iOS 14+)
92
+ * IDFV is always available on iOS
93
+ */
94
+ getAdvertiserInfo(): Promise<AdvertiserInfo | null>;
95
+ };
81
96
  export declare const PlayInstallReferrerNativeBridge: {
82
97
  /**
83
98
  * Check if Play Install Referrer is available
@@ -219,6 +219,24 @@ export const AppleSearchAdsNativeBridge = {
219
219
  }
220
220
  },
221
221
  };
222
+ export const AdvertiserInfoBridge = {
223
+ /**
224
+ * Get advertiser info (IDFA, IDFV, ATT status)
225
+ * IDFA is only available when ATT is authorized (iOS 14+)
226
+ * IDFV is always available on iOS
227
+ */
228
+ async getAdvertiserInfo() {
229
+ if (!DatalyrNative)
230
+ return null;
231
+ try {
232
+ return await DatalyrNative.getAdvertiserInfo();
233
+ }
234
+ catch (error) {
235
+ console.error('[Datalyr/AdvertiserInfo] Get advertiser info failed:', error);
236
+ return null;
237
+ }
238
+ },
239
+ };
222
240
  // MARK: - Play Install Referrer Bridge (Android only)
223
241
  export const PlayInstallReferrerNativeBridge = {
224
242
  /**
@@ -2,4 +2,5 @@
2
2
  * Native Module Exports
3
3
  */
4
4
  export { SKAdNetworkBridge } from './SKAdNetworkBridge';
5
- export { isNativeModuleAvailable, getSDKAvailability, MetaNativeBridge, TikTokNativeBridge, } from './DatalyrNativeBridge';
5
+ export { isNativeModuleAvailable, getSDKAvailability, MetaNativeBridge, TikTokNativeBridge, AdvertiserInfoBridge, } from './DatalyrNativeBridge';
6
+ export type { AdvertiserInfo } from './DatalyrNativeBridge';
@@ -2,4 +2,4 @@
2
2
  * Native Module Exports
3
3
  */
4
4
  export { SKAdNetworkBridge } from './SKAdNetworkBridge';
5
- export { isNativeModuleAvailable, getSDKAvailability, MetaNativeBridge, TikTokNativeBridge, } from './DatalyrNativeBridge';
5
+ export { isNativeModuleAvailable, getSDKAvailability, MetaNativeBridge, TikTokNativeBridge, AdvertiserInfoBridge, } from './DatalyrNativeBridge';
package/lib/types.d.ts CHANGED
@@ -83,9 +83,9 @@ export interface DatalyrConfig {
83
83
  maxEventQueueSize?: number;
84
84
  /** Respect browser Do Not Track setting. Default: true */
85
85
  respectDoNotTrack?: boolean;
86
- /** Enable automatic event tracking (sessions, app lifecycle). Default: false */
86
+ /** Enable automatic event tracking (sessions, app lifecycle). Default: true */
87
87
  enableAutoEvents?: boolean;
88
- /** Enable attribution tracking (deep links, install referrer). Default: false */
88
+ /** Enable attribution tracking (deep links, install referrer). Default: true */
89
89
  enableAttribution?: boolean;
90
90
  /** Enable web-to-app attribution matching via email. Default: true */
91
91
  enableWebToAppAttribution?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalyr/react-native",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "Datalyr SDK for React Native & Expo - Server-side attribution tracking with bundled Meta and TikTok SDKs for iOS and Android",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -34,9 +34,9 @@ import { journeyManager } from './journey';
34
34
  import { createAutoEventsManager, AutoEventsManager } from './auto-events';
35
35
  import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
36
36
  import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
37
- import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
37
+ import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
38
38
  import { DeferredDeepLinkResult } from './types';
39
- import { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
39
+ import { AppleSearchAdsAttribution, AdvertiserInfoBridge } from './native/DatalyrNativeBridge';
40
40
 
41
41
  export class DatalyrSDKExpo {
42
42
  private state: SDKState;
@@ -44,6 +44,7 @@ export class DatalyrSDKExpo {
44
44
  private eventQueue: EventQueue;
45
45
  private autoEventsManager: AutoEventsManager | null = null;
46
46
  private appStateSubscription: any = null;
47
+ private cachedAdvertiserInfo: any = null;
47
48
  private static conversionEncoder?: ConversionValueEncoder;
48
49
  private static debugEnabled = false;
49
50
 
@@ -59,9 +60,11 @@ export class DatalyrSDKExpo {
59
60
  maxRetries: 3,
60
61
  retryDelay: 1000,
61
62
  batchSize: 10,
62
- flushInterval: 10000,
63
+ flushInterval: 30000,
63
64
  maxQueueSize: 100,
64
65
  respectDoNotTrack: true,
66
+ enableAutoEvents: true,
67
+ enableAttribution: true,
65
68
  },
66
69
  visitorId: '',
67
70
  anonymousId: '',
@@ -198,10 +201,22 @@ export class DatalyrSDKExpo {
198
201
 
199
202
  // Initialize Apple Search Ads attribution (iOS only, auto-fetches on init)
200
203
  await appleSearchAdsIntegration.initialize(config.debug);
204
+
205
+ // Initialize Play Install Referrer (Android only)
206
+ await playInstallReferrerIntegration.initialize();
207
+
208
+ // Cache advertiser info (IDFA/GAID, ATT status) once at init to avoid per-event native bridge calls
209
+ try {
210
+ this.cachedAdvertiserInfo = await AdvertiserInfoBridge.getAdvertiserInfo();
211
+ } catch (error) {
212
+ errorLog('Failed to cache advertiser info:', error as Error);
213
+ }
214
+
201
215
  debugLog('Platform integrations initialized', {
202
216
  meta: metaIntegration.isAvailable(),
203
217
  tiktok: tiktokIntegration.isAvailable(),
204
218
  appleSearchAds: appleSearchAdsIntegration.isAvailable(),
219
+ playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
205
220
  });
206
221
 
207
222
  this.state.initialized = true;
@@ -210,7 +225,7 @@ export class DatalyrSDKExpo {
210
225
  const installData = await attributionManager.trackInstall();
211
226
  await this.track('app_install', {
212
227
  platform: Platform.OS,
213
- sdk_version: '1.1.0',
228
+ sdk_version: '1.4.9',
214
229
  sdk_variant: 'expo',
215
230
  ...installData,
216
231
  });
@@ -694,14 +709,23 @@ export class DatalyrSDKExpo {
694
709
  return null;
695
710
  }
696
711
 
697
- getPlatformIntegrationStatus(): { meta: boolean; tiktok: boolean; appleSearchAds: boolean } {
712
+ getPlatformIntegrationStatus(): { meta: boolean; tiktok: boolean; appleSearchAds: boolean; playInstallReferrer: boolean } {
698
713
  return {
699
714
  meta: metaIntegration.isAvailable(),
700
715
  tiktok: tiktokIntegration.isAvailable(),
701
716
  appleSearchAds: appleSearchAdsIntegration.isAvailable(),
717
+ playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
702
718
  };
703
719
  }
704
720
 
721
+ /**
722
+ * Get Play Install Referrer data (Android only)
723
+ */
724
+ getPlayInstallReferrer(): Record<string, any> | null {
725
+ const data = playInstallReferrerIntegration.getReferrerData();
726
+ return data ? playInstallReferrerIntegration.getAttributionData() : null;
727
+ }
728
+
705
729
  /**
706
730
  * Get Apple Search Ads attribution data
707
731
  * Returns attribution if user installed via Apple Search Ads, null otherwise
@@ -717,6 +741,13 @@ export class DatalyrSDKExpo {
717
741
  if (tiktokIntegration.isAvailable()) {
718
742
  tiktokIntegration.updateTrackingAuthorization(authorized);
719
743
  }
744
+
745
+ // Refresh cached advertiser info after ATT status change
746
+ try {
747
+ this.cachedAdvertiserInfo = await AdvertiserInfoBridge.getAdvertiserInfo();
748
+ } catch (error) {
749
+ errorLog('Failed to refresh advertiser info:', error as Error);
750
+ }
720
751
  }
721
752
 
722
753
  private async handleDeferredDeepLink(data: DeferredDeepLinkResult): Promise<void> {
@@ -759,7 +790,7 @@ export class DatalyrSDKExpo {
759
790
  const deviceInfo = await getDeviceInfo();
760
791
  const fingerprintData = await createFingerprintData();
761
792
  const attributionData = attributionManager.getAttributionData();
762
- const networkType = await getNetworkType();
793
+ const networkType = getNetworkType();
763
794
 
764
795
  // Get Apple Search Ads attribution if available
765
796
  const asaAttribution = appleSearchAdsIntegration.getAttributionData();
@@ -777,6 +808,9 @@ export class DatalyrSDKExpo {
777
808
  asa_country_or_region: asaAttribution.countryOrRegion,
778
809
  } : {};
779
810
 
811
+ // Use cached advertiser info (IDFA/GAID, ATT status) — cached at init, refreshed on ATT change
812
+ const advertiserInfo = this.cachedAdvertiserInfo;
813
+
780
814
  const payload: EventPayload = {
781
815
  workspaceId: this.state.config.workspaceId || 'mobile_sdk',
782
816
  visitorId: this.state.visitorId,
@@ -792,9 +826,25 @@ export class DatalyrSDKExpo {
792
826
  device_model: deviceInfo.model,
793
827
  app_version: deviceInfo.appVersion,
794
828
  app_build: deviceInfo.buildNumber,
829
+ app_name: deviceInfo.bundleId,
830
+ app_namespace: deviceInfo.bundleId,
831
+ screen_width: deviceInfo.screenWidth,
832
+ screen_height: deviceInfo.screenHeight,
833
+ locale: deviceInfo.locale,
834
+ timezone: deviceInfo.timezone,
835
+ carrier: deviceInfo.carrier,
795
836
  network_type: networkType,
796
837
  timestamp: Date.now(),
838
+ sdk_version: '1.4.9',
797
839
  sdk_variant: 'expo',
840
+ // Advertiser data (IDFA/GAID, ATT status) for Meta CAPI / TikTok Events API
841
+ ...(advertiserInfo ? {
842
+ idfa: advertiserInfo.idfa,
843
+ idfv: advertiserInfo.idfv,
844
+ gaid: advertiserInfo.gaid,
845
+ att_status: advertiserInfo.att_status,
846
+ advertiser_tracking_enabled: advertiserInfo.advertiser_tracking_enabled,
847
+ } : {}),
798
848
  ...attributionData,
799
849
  // Apple Search Ads attribution
800
850
  ...asaData,
@@ -1030,10 +1080,14 @@ export class DatalyrExpo {
1030
1080
  return datalyrExpo.getDeferredAttributionData();
1031
1081
  }
1032
1082
 
1033
- static getPlatformIntegrationStatus(): { meta: boolean; tiktok: boolean; appleSearchAds: boolean } {
1083
+ static getPlatformIntegrationStatus(): { meta: boolean; tiktok: boolean; appleSearchAds: boolean; playInstallReferrer: boolean } {
1034
1084
  return datalyrExpo.getPlatformIntegrationStatus();
1035
1085
  }
1036
1086
 
1087
+ static getPlayInstallReferrer(): Record<string, any> | null {
1088
+ return datalyrExpo.getPlayInstallReferrer();
1089
+ }
1090
+
1037
1091
  static getAppleSearchAdsAttribution(): AppleSearchAdsAttribution | null {
1038
1092
  return datalyrExpo.getAppleSearchAdsAttribution();
1039
1093
  }
@@ -32,7 +32,7 @@ import { createAutoEventsManager, AutoEventsManager, SessionData } from './auto-
32
32
  import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
33
33
  import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
34
34
  import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
35
- import { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
35
+ import { AppleSearchAdsAttribution, AdvertiserInfoBridge } from './native/DatalyrNativeBridge';
36
36
  import { networkStatusManager } from './network-status';
37
37
 
38
38
  export class DatalyrSDK {
@@ -42,6 +42,7 @@ export class DatalyrSDK {
42
42
  private autoEventsManager: AutoEventsManager | null = null;
43
43
  private appStateSubscription: any = null;
44
44
  private networkStatusUnsubscribe: (() => void) | null = null;
45
+ private cachedAdvertiserInfo: any = null;
45
46
  private static conversionEncoder?: ConversionValueEncoder;
46
47
  private static debugEnabled = false;
47
48
 
@@ -58,9 +59,11 @@ export class DatalyrSDK {
58
59
  maxRetries: 3,
59
60
  retryDelay: 1000,
60
61
  batchSize: 10,
61
- flushInterval: 10000,
62
+ flushInterval: 30000,
62
63
  maxQueueSize: 100,
63
64
  respectDoNotTrack: true,
65
+ enableAutoEvents: true,
66
+ enableAttribution: true,
64
67
  },
65
68
  visitorId: '',
66
69
  anonymousId: '', // Persistent anonymous identifier
@@ -221,6 +224,13 @@ export class DatalyrSDK {
221
224
  // Wait for all platform integrations to complete
222
225
  await Promise.all(platformInitPromises);
223
226
 
227
+ // Cache advertiser info (IDFA/GAID, ATT status) once at init to avoid per-event native bridge calls
228
+ try {
229
+ this.cachedAdvertiserInfo = await AdvertiserInfoBridge.getAdvertiserInfo();
230
+ } catch (error) {
231
+ errorLog('Failed to cache advertiser info:', error as Error);
232
+ }
233
+
224
234
  debugLog('Platform integrations initialized', {
225
235
  meta: metaIntegration.isAvailable(),
226
236
  tiktok: tiktokIntegration.isAvailable(),
@@ -236,7 +246,7 @@ export class DatalyrSDK {
236
246
  const installData = await attributionManager.trackInstall();
237
247
  await this.track('app_install', {
238
248
  platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
239
- sdk_version: '1.0.2',
249
+ sdk_version: '1.4.9',
240
250
  ...installData,
241
251
  });
242
252
  }
@@ -939,6 +949,13 @@ export class DatalyrSDK {
939
949
  metaIntegration.updateTrackingAuthorization(enabled);
940
950
  tiktokIntegration.updateTrackingAuthorization(enabled);
941
951
 
952
+ // Refresh cached advertiser info after ATT status change
953
+ try {
954
+ this.cachedAdvertiserInfo = await AdvertiserInfoBridge.getAdvertiserInfo();
955
+ } catch (error) {
956
+ errorLog('Failed to refresh advertiser info:', error as Error);
957
+ }
958
+
942
959
  // Track ATT status event
943
960
  await this.track('$att_status', {
944
961
  authorized: enabled,
@@ -1022,6 +1039,9 @@ export class DatalyrSDK {
1022
1039
  asa_country_or_region: asaAttribution.countryOrRegion,
1023
1040
  } : {};
1024
1041
 
1042
+ // Use cached advertiser info (IDFA/GAID, ATT status) — cached at init, refreshed on ATT change
1043
+ const advertiserInfo = this.cachedAdvertiserInfo;
1044
+
1025
1045
  const payload: EventPayload = {
1026
1046
  workspaceId: this.state.config.workspaceId || 'mobile_sdk',
1027
1047
  visitorId: this.state.visitorId,
@@ -1039,8 +1059,24 @@ export class DatalyrSDK {
1039
1059
  device_model: deviceInfo.model,
1040
1060
  app_version: deviceInfo.appVersion,
1041
1061
  app_build: deviceInfo.buildNumber,
1062
+ app_name: deviceInfo.bundleId, // Best available app name
1063
+ app_namespace: deviceInfo.bundleId,
1064
+ screen_width: deviceInfo.screenWidth,
1065
+ screen_height: deviceInfo.screenHeight,
1066
+ locale: deviceInfo.locale,
1067
+ timezone: deviceInfo.timezone,
1068
+ carrier: deviceInfo.carrier,
1042
1069
  network_type: getNetworkType(),
1043
1070
  timestamp: Date.now(),
1071
+ sdk_version: '1.4.9',
1072
+ // Advertiser data (IDFA/GAID, ATT status) for Meta CAPI / TikTok Events API
1073
+ ...(advertiserInfo ? {
1074
+ idfa: advertiserInfo.idfa,
1075
+ idfv: advertiserInfo.idfv,
1076
+ gaid: advertiserInfo.gaid,
1077
+ att_status: advertiserInfo.att_status,
1078
+ advertiser_tracking_enabled: advertiserInfo.advertiser_tracking_enabled,
1079
+ } : {}),
1044
1080
  // Attribution data
1045
1081
  ...attributionData,
1046
1082
  // Apple Search Ads attribution
@@ -262,7 +262,7 @@ export const createEventQueue = (httpClient: HttpClient, config?: Partial<QueueC
262
262
  const defaultConfig: QueueConfig = {
263
263
  maxQueueSize: 100,
264
264
  batchSize: 10,
265
- flushInterval: 10000, // 10 seconds
265
+ flushInterval: 30000, // 30 seconds — matches SDK constructor defaults and docs
266
266
  maxRetryCount: 3,
267
267
  };
268
268
 
@@ -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.0.2`,
75
+ 'User-Agent': `@datalyr/react-native/1.4.8`,
76
76
  };
77
77
 
78
78
  // Server-side tracking uses X-API-Key header
@@ -188,7 +188,7 @@ export class HttpClient {
188
188
  return {
189
189
  event: payload.eventName,
190
190
  userId: payload.userId || payload.visitorId,
191
- anonymousId: payload.visitorId,
191
+ anonymousId: payload.anonymousId || payload.visitorId,
192
192
  properties: {
193
193
  ...payload.eventData,
194
194
  sessionId: payload.sessionId,
@@ -197,7 +197,7 @@ export class HttpClient {
197
197
  },
198
198
  context: {
199
199
  library: '@datalyr/react-native',
200
- version: '1.0.5',
200
+ version: '1.4.8',
201
201
  source: 'mobile_app', // Explicitly set source for mobile
202
202
  userProperties: payload.userProperties,
203
203
  },
@@ -76,9 +76,11 @@ export class TikTokIntegration {
76
76
  if (success) {
77
77
  this.initialized = true;
78
78
  this.log(`TikTok SDK initialized with App ID: ${config.tiktokAppId}`);
79
+ } else {
80
+ console.warn('[Datalyr/TikTok] TikTok SDK not initialized (accessToken may be missing). Events will still be sent server-side via Datalyr postbacks.');
79
81
  }
80
82
  } catch (error) {
81
- this.logError('Failed to initialize TikTok SDK:', error);
83
+ console.warn('[Datalyr/TikTok] TikTok SDK init failed. Events will still be sent server-side via Datalyr postbacks.', error);
82
84
  }
83
85
  }
84
86
 
@@ -90,6 +90,15 @@ interface DatalyrNativeModule {
90
90
  logoutTikTok(): Promise<boolean>;
91
91
  updateTikTokTrackingAuthorization(enabled: boolean): Promise<boolean>;
92
92
 
93
+ // Advertiser Info (IDFA, IDFV, GAID, ATT Status)
94
+ getAdvertiserInfo(): Promise<{
95
+ idfa?: string;
96
+ idfv?: string;
97
+ gaid?: string;
98
+ att_status: number;
99
+ advertiser_tracking_enabled: boolean;
100
+ } | null>;
101
+
93
102
  // Apple Search Ads Methods (iOS only)
94
103
  getAppleSearchAdsAttribution(): Promise<AppleSearchAdsAttribution | null>;
95
104
 
@@ -374,6 +383,34 @@ export const AppleSearchAdsNativeBridge = {
374
383
  },
375
384
  };
376
385
 
386
+ // MARK: - Advertiser Info Bridge
387
+
388
+ export interface AdvertiserInfo {
389
+ idfa?: string;
390
+ idfv?: string;
391
+ gaid?: string;
392
+ att_status: number;
393
+ advertiser_tracking_enabled: boolean;
394
+ }
395
+
396
+ export const AdvertiserInfoBridge = {
397
+ /**
398
+ * Get advertiser info (IDFA, IDFV, ATT status)
399
+ * IDFA is only available when ATT is authorized (iOS 14+)
400
+ * IDFV is always available on iOS
401
+ */
402
+ async getAdvertiserInfo(): Promise<AdvertiserInfo | null> {
403
+ if (!DatalyrNative) return null;
404
+
405
+ try {
406
+ return await DatalyrNative.getAdvertiserInfo();
407
+ } catch (error) {
408
+ console.error('[Datalyr/AdvertiserInfo] Get advertiser info failed:', error);
409
+ return null;
410
+ }
411
+ },
412
+ };
413
+
377
414
  // MARK: - Play Install Referrer Bridge (Android only)
378
415
 
379
416
  export const PlayInstallReferrerNativeBridge = {
@@ -8,4 +8,6 @@ export {
8
8
  getSDKAvailability,
9
9
  MetaNativeBridge,
10
10
  TikTokNativeBridge,
11
+ AdvertiserInfoBridge,
11
12
  } from './DatalyrNativeBridge';
13
+ export type { AdvertiserInfo } from './DatalyrNativeBridge';
package/src/types.ts CHANGED
@@ -105,10 +105,10 @@ export interface DatalyrConfig {
105
105
  /** Respect browser Do Not Track setting. Default: true */
106
106
  respectDoNotTrack?: boolean;
107
107
 
108
- /** Enable automatic event tracking (sessions, app lifecycle). Default: false */
108
+ /** Enable automatic event tracking (sessions, app lifecycle). Default: true */
109
109
  enableAutoEvents?: boolean;
110
110
 
111
- /** Enable attribution tracking (deep links, install referrer). Default: false */
111
+ /** Enable attribution tracking (deep links, install referrer). Default: true */
112
112
  enableAttribution?: boolean;
113
113
 
114
114
  /** Enable web-to-app attribution matching via email. Default: true */
package/src/utils-expo.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import AsyncStorage from '@react-native-async-storage/async-storage';
2
- import { Platform } from 'react-native';
2
+ import { Platform, Dimensions } from 'react-native';
3
3
  import * as Application from 'expo-application';
4
4
  import * as Device from 'expo-device';
5
5
  import * as Network from 'expo-network';
@@ -84,10 +84,14 @@ export interface DeviceInfo {
84
84
  isEmulator: boolean;
85
85
  }
86
86
 
87
- export const getDeviceInfo = async (): Promise<DeviceInfo> => {
87
+ // Cached device info to avoid repeated async calls (matches utils.ts pattern)
88
+ let cachedDeviceInfo: DeviceInfo | null = null;
89
+ let deviceInfoPromise: Promise<DeviceInfo> | null = null;
90
+
91
+ const fetchDeviceInfoInternal = async (): Promise<DeviceInfo> => {
88
92
  try {
89
93
  const deviceId = await getOrCreateDeviceId();
90
-
94
+
91
95
  return {
92
96
  deviceId,
93
97
  model: Device.modelName || Device.deviceName || 'Unknown',
@@ -96,10 +100,10 @@ export const getDeviceInfo = async (): Promise<DeviceInfo> => {
96
100
  appVersion: Application.nativeApplicationVersion || '1.0.0',
97
101
  buildNumber: Application.nativeBuildVersion || '1',
98
102
  bundleId: Application.applicationId || 'unknown.bundle.id',
99
- screenWidth: 0, // Would need Dimensions from react-native
100
- screenHeight: 0, // Would need Dimensions from react-native
103
+ screenWidth: Dimensions.get('window').width,
104
+ screenHeight: Dimensions.get('window').height,
101
105
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
102
- locale: 'en-US', // Would need expo-localization for full locale
106
+ locale: Intl.DateTimeFormat().resolvedOptions().locale || 'en-US',
103
107
  carrier: undefined, // Not available in Expo managed workflow
104
108
  isEmulator: !Device.isDevice,
105
109
  };
@@ -113,8 +117,8 @@ export const getDeviceInfo = async (): Promise<DeviceInfo> => {
113
117
  appVersion: '1.0.0',
114
118
  buildNumber: '1',
115
119
  bundleId: 'unknown.bundle.id',
116
- screenWidth: 0,
117
- screenHeight: 0,
120
+ screenWidth: Dimensions.get('window').width,
121
+ screenHeight: Dimensions.get('window').height,
118
122
  timezone: 'UTC',
119
123
  locale: 'en-US',
120
124
  isEmulator: true,
@@ -122,6 +126,19 @@ export const getDeviceInfo = async (): Promise<DeviceInfo> => {
122
126
  }
123
127
  };
124
128
 
129
+ export const getDeviceInfo = async (): Promise<DeviceInfo> => {
130
+ if (cachedDeviceInfo) return cachedDeviceInfo;
131
+ if (deviceInfoPromise) return deviceInfoPromise;
132
+
133
+ deviceInfoPromise = fetchDeviceInfoInternal();
134
+ try {
135
+ cachedDeviceInfo = await deviceInfoPromise;
136
+ return cachedDeviceInfo;
137
+ } finally {
138
+ deviceInfoPromise = null;
139
+ }
140
+ };
141
+
125
142
  // Device ID management
126
143
  const getOrCreateDeviceId = async (): Promise<string> => {
127
144
  try {
@@ -246,30 +263,49 @@ export const createFingerprintData = async () => {
246
263
  // IDFA/GAID collection has been removed for privacy compliance
247
264
  // Modern attribution tracking relies on privacy-safe methods:
248
265
 
249
- // Network type detection using Expo Network
250
- export const getNetworkType = async (): Promise<string> => {
266
+ // Cached network type to avoid per-event native bridge calls
267
+ let cachedNetworkType = 'unknown';
268
+ let networkTypeLastFetched = 0;
269
+ const NETWORK_TYPE_CACHE_MS = 30000; // Refresh every 30s
270
+
271
+ // Network type detection using Expo Network — cached to avoid per-event async calls
272
+ export const getNetworkType = (): string => {
273
+ // Trigger background refresh if stale, but always return cached value synchronously
274
+ const now = Date.now();
275
+ if (now - networkTypeLastFetched > NETWORK_TYPE_CACHE_MS) {
276
+ networkTypeLastFetched = now;
277
+ refreshNetworkType();
278
+ }
279
+ return cachedNetworkType;
280
+ };
281
+
282
+ const refreshNetworkType = async (): Promise<void> => {
251
283
  try {
252
284
  const networkState = await Network.getNetworkStateAsync();
253
-
285
+
254
286
  if (!networkState.isConnected) {
255
- return 'none';
287
+ cachedNetworkType = 'none';
288
+ return;
256
289
  }
257
-
290
+
258
291
  switch (networkState.type) {
259
292
  case Network.NetworkStateType.WIFI:
260
- return 'wifi';
293
+ cachedNetworkType = 'wifi';
294
+ break;
261
295
  case Network.NetworkStateType.CELLULAR:
262
- return 'cellular';
296
+ cachedNetworkType = 'cellular';
297
+ break;
263
298
  case Network.NetworkStateType.ETHERNET:
264
- return 'ethernet';
299
+ cachedNetworkType = 'ethernet';
300
+ break;
265
301
  case Network.NetworkStateType.BLUETOOTH:
266
- return 'bluetooth';
302
+ cachedNetworkType = 'bluetooth';
303
+ break;
267
304
  default:
268
- return 'unknown';
305
+ cachedNetworkType = 'unknown';
269
306
  }
270
307
  } catch (error) {
271
308
  debugLog('Error getting network type:', error);
272
- return 'unknown';
273
309
  }
274
310
  };
275
311