@datalyr/react-native 1.7.4 → 1.7.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/lib/datalyr-sdk.js +5 -5
- package/lib/http-client.js +2 -2
- package/lib/types.d.ts +2 -2
- package/lib/utils.d.ts +3 -3
- package/lib/utils.js +2 -2
- package/package.json +1 -1
- package/src/datalyr-sdk-expo.ts +91 -5
- package/src/datalyr-sdk.ts +5 -5
- package/src/expo.ts +1 -1
- package/src/http-client.ts +2 -2
- package/src/types.ts +2 -2
- package/src/utils-expo.ts +3 -3
- package/src/utils-interface.ts +2 -2
- package/src/utils.ts +3 -3
package/lib/datalyr-sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Platform, AppState } from 'react-native';
|
|
2
|
-
import { getOrCreateVisitorId, getOrCreateAnonymousId, getOrCreateSessionId,
|
|
2
|
+
import { getOrCreateVisitorId, getOrCreateAnonymousId, getOrCreateSessionId, createDeviceContext, generateUUID, getDeviceInfo, getNetworkType, validateEventName, validateEventData, debugLog, errorLog, Storage, STORAGE_KEYS, } from './utils';
|
|
3
3
|
import { createHttpClient, HttpClient } from './http-client';
|
|
4
4
|
import { createEventQueue, EventQueue } from './event-queue';
|
|
5
5
|
import { attributionManager } from './attribution';
|
|
@@ -188,7 +188,7 @@ export class DatalyrSDK {
|
|
|
188
188
|
const installData = await attributionManager.trackInstall();
|
|
189
189
|
await this.track('app_install', {
|
|
190
190
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
191
|
-
sdk_version: '1.7.
|
|
191
|
+
sdk_version: '1.7.5',
|
|
192
192
|
...installData,
|
|
193
193
|
});
|
|
194
194
|
}
|
|
@@ -874,7 +874,7 @@ export class DatalyrSDK {
|
|
|
874
874
|
*/
|
|
875
875
|
async createEventPayload(eventName, eventData) {
|
|
876
876
|
const deviceInfo = await getDeviceInfo();
|
|
877
|
-
const
|
|
877
|
+
const deviceContext = await createDeviceContext();
|
|
878
878
|
const attributionData = attributionManager.getAttributionData();
|
|
879
879
|
// Get Apple Search Ads attribution if available
|
|
880
880
|
const asaAttribution = appleSearchAdsIntegration.getAttributionData();
|
|
@@ -919,7 +919,7 @@ export class DatalyrSDK {
|
|
|
919
919
|
carrier: deviceInfo.carrier,
|
|
920
920
|
network_type: getNetworkType(),
|
|
921
921
|
timestamp: Date.now(),
|
|
922
|
-
sdk_version: '1.7.
|
|
922
|
+
sdk_version: '1.7.5',
|
|
923
923
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
924
924
|
...(advertiserInfo ? {
|
|
925
925
|
idfa: advertiserInfo.idfa,
|
|
@@ -933,7 +933,7 @@ export class DatalyrSDK {
|
|
|
933
933
|
// Apple Search Ads attribution
|
|
934
934
|
...asaData,
|
|
935
935
|
},
|
|
936
|
-
|
|
936
|
+
deviceContext,
|
|
937
937
|
source: 'mobile_app',
|
|
938
938
|
timestamp: new Date().toISOString(),
|
|
939
939
|
};
|
package/lib/http-client.js
CHANGED
|
@@ -149,11 +149,11 @@ export class HttpClient {
|
|
|
149
149
|
...payload.eventData,
|
|
150
150
|
sessionId: payload.sessionId,
|
|
151
151
|
source: payload.source || 'mobile_app',
|
|
152
|
-
fingerprint: payload.
|
|
152
|
+
fingerprint: payload.deviceContext,
|
|
153
153
|
},
|
|
154
154
|
context: {
|
|
155
155
|
library: '@datalyr/react-native',
|
|
156
|
-
version: '1.
|
|
156
|
+
version: '1.7.5',
|
|
157
157
|
source: 'mobile_app',
|
|
158
158
|
userProperties: payload.userProperties,
|
|
159
159
|
},
|
package/lib/types.d.ts
CHANGED
|
@@ -106,7 +106,7 @@ export interface EventData {
|
|
|
106
106
|
app_build?: string;
|
|
107
107
|
network_type?: string;
|
|
108
108
|
}
|
|
109
|
-
export interface
|
|
109
|
+
export interface DeviceContext {
|
|
110
110
|
deviceId?: string;
|
|
111
111
|
deviceInfo?: {
|
|
112
112
|
model: string;
|
|
@@ -127,7 +127,7 @@ export interface EventPayload {
|
|
|
127
127
|
eventId: string;
|
|
128
128
|
eventName: string;
|
|
129
129
|
eventData?: EventData;
|
|
130
|
-
|
|
130
|
+
deviceContext?: DeviceContext;
|
|
131
131
|
source: 'mobile_app';
|
|
132
132
|
timestamp: string;
|
|
133
133
|
userId?: string;
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'react-native-get-random-values';
|
|
2
|
-
import { DeviceInfo as DeviceInfoType,
|
|
2
|
+
import { DeviceInfo as DeviceInfoType, DeviceContext } from './types';
|
|
3
3
|
export declare const STORAGE_KEYS: {
|
|
4
4
|
VISITOR_ID: string;
|
|
5
5
|
ANONYMOUS_ID: string;
|
|
@@ -45,9 +45,9 @@ export declare const getDeviceInfo: () => Promise<DeviceInfoType>;
|
|
|
45
45
|
*/
|
|
46
46
|
export declare const clearDeviceInfoCache: () => void;
|
|
47
47
|
/**
|
|
48
|
-
* Create
|
|
48
|
+
* Create device context for attribution
|
|
49
49
|
*/
|
|
50
|
-
export declare const
|
|
50
|
+
export declare const createDeviceContext: () => Promise<DeviceContext>;
|
|
51
51
|
/**
|
|
52
52
|
* Get network connection type
|
|
53
53
|
*/
|
package/lib/utils.js
CHANGED
|
@@ -219,9 +219,9 @@ const fetchDeviceInfoInternal = async () => {
|
|
|
219
219
|
}
|
|
220
220
|
};
|
|
221
221
|
/**
|
|
222
|
-
* Create
|
|
222
|
+
* Create device context for attribution
|
|
223
223
|
*/
|
|
224
|
-
export const
|
|
224
|
+
export const createDeviceContext = async () => {
|
|
225
225
|
const deviceInfo = await getDeviceInfo();
|
|
226
226
|
return {
|
|
227
227
|
deviceId: deviceInfo.deviceId,
|
package/package.json
CHANGED
package/src/datalyr-sdk-expo.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
getOrCreateVisitorId,
|
|
17
17
|
getOrCreateAnonymousId,
|
|
18
18
|
getOrCreateSessionId,
|
|
19
|
-
|
|
19
|
+
createDeviceContext,
|
|
20
20
|
generateUUID,
|
|
21
21
|
getDeviceInfo,
|
|
22
22
|
getNetworkType,
|
|
@@ -206,10 +206,15 @@ export class DatalyrSDKExpo {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
if (attributionManager.isInstall()) {
|
|
209
|
+
// iOS: Attempt deferred web-to-app attribution via IP matching before tracking install
|
|
210
|
+
if (Platform.OS === 'ios') {
|
|
211
|
+
await this.fetchDeferredWebAttribution();
|
|
212
|
+
}
|
|
213
|
+
|
|
209
214
|
const installData = await attributionManager.trackInstall();
|
|
210
215
|
await this.track('app_install', {
|
|
211
216
|
platform: Platform.OS,
|
|
212
|
-
sdk_version: '1.7.
|
|
217
|
+
sdk_version: '1.7.5',
|
|
213
218
|
sdk_variant: 'expo',
|
|
214
219
|
...installData,
|
|
215
220
|
});
|
|
@@ -376,6 +381,87 @@ export class DatalyrSDKExpo {
|
|
|
376
381
|
}
|
|
377
382
|
}
|
|
378
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Fetch deferred web attribution on first app install.
|
|
386
|
+
* Uses IP-based matching to recover attribution data (fbclid, utm_*, etc.)
|
|
387
|
+
* from a prelander web visit. Called automatically during initialize()
|
|
388
|
+
* when a fresh install is detected on iOS.
|
|
389
|
+
*/
|
|
390
|
+
private async fetchDeferredWebAttribution(): Promise<void> {
|
|
391
|
+
if (!this.state.config?.apiKey) {
|
|
392
|
+
debugLog('API key not available for deferred attribution fetch');
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
debugLog('Fetching deferred web attribution via IP matching...');
|
|
398
|
+
|
|
399
|
+
const controller = new AbortController();
|
|
400
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
401
|
+
|
|
402
|
+
const response = await fetch('https://api.datalyr.com/attribution/deferred-lookup', {
|
|
403
|
+
method: 'POST',
|
|
404
|
+
headers: {
|
|
405
|
+
'Content-Type': 'application/json',
|
|
406
|
+
'X-Datalyr-API-Key': this.state.config.apiKey!,
|
|
407
|
+
},
|
|
408
|
+
body: JSON.stringify({ platform: Platform.OS }),
|
|
409
|
+
signal: controller.signal,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
clearTimeout(timeout);
|
|
413
|
+
|
|
414
|
+
if (!response.ok) {
|
|
415
|
+
debugLog('Deferred attribution lookup failed:', response.status);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const result = await response.json() as { found: boolean; attribution?: any };
|
|
420
|
+
|
|
421
|
+
if (!result.found || !result.attribution) {
|
|
422
|
+
debugLog('No deferred web attribution found for this IP');
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const webAttribution = result.attribution;
|
|
427
|
+
debugLog('Deferred web attribution found:', {
|
|
428
|
+
visitor_id: webAttribution.visitor_id,
|
|
429
|
+
has_fbclid: !!webAttribution.fbclid,
|
|
430
|
+
has_gclid: !!webAttribution.gclid,
|
|
431
|
+
utm_source: webAttribution.utm_source,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Merge web attribution into current session
|
|
435
|
+
attributionManager.mergeWebAttribution(webAttribution);
|
|
436
|
+
|
|
437
|
+
// Track match event for analytics
|
|
438
|
+
await this.track('$web_attribution_matched', {
|
|
439
|
+
web_visitor_id: webAttribution.visitor_id,
|
|
440
|
+
web_user_id: webAttribution.user_id,
|
|
441
|
+
fbclid: webAttribution.fbclid,
|
|
442
|
+
gclid: webAttribution.gclid,
|
|
443
|
+
ttclid: webAttribution.ttclid,
|
|
444
|
+
gbraid: webAttribution.gbraid,
|
|
445
|
+
wbraid: webAttribution.wbraid,
|
|
446
|
+
fbp: webAttribution.fbp,
|
|
447
|
+
fbc: webAttribution.fbc,
|
|
448
|
+
utm_source: webAttribution.utm_source,
|
|
449
|
+
utm_medium: webAttribution.utm_medium,
|
|
450
|
+
utm_campaign: webAttribution.utm_campaign,
|
|
451
|
+
utm_content: webAttribution.utm_content,
|
|
452
|
+
utm_term: webAttribution.utm_term,
|
|
453
|
+
web_timestamp: webAttribution.timestamp,
|
|
454
|
+
match_method: 'ip',
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
debugLog('Successfully merged deferred web attribution');
|
|
458
|
+
|
|
459
|
+
} catch (error) {
|
|
460
|
+
errorLog('Error fetching deferred web attribution:', error as Error);
|
|
461
|
+
// Non-blocking - email-based fallback will catch this on identify()
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
379
465
|
async alias(newUserId: string, previousId?: string): Promise<void> {
|
|
380
466
|
try {
|
|
381
467
|
if (!newUserId || typeof newUserId !== 'string') {
|
|
@@ -758,7 +844,7 @@ export class DatalyrSDKExpo {
|
|
|
758
844
|
|
|
759
845
|
private async createEventPayload(eventName: string, eventData?: EventData): Promise<EventPayload> {
|
|
760
846
|
const deviceInfo = await getDeviceInfo();
|
|
761
|
-
const
|
|
847
|
+
const deviceContext = await createDeviceContext();
|
|
762
848
|
const attributionData = attributionManager.getAttributionData();
|
|
763
849
|
const networkType = getNetworkType();
|
|
764
850
|
|
|
@@ -805,7 +891,7 @@ export class DatalyrSDKExpo {
|
|
|
805
891
|
carrier: deviceInfo.carrier,
|
|
806
892
|
network_type: networkType,
|
|
807
893
|
timestamp: Date.now(),
|
|
808
|
-
sdk_version: '1.7.
|
|
894
|
+
sdk_version: '1.7.5',
|
|
809
895
|
sdk_variant: 'expo',
|
|
810
896
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
811
897
|
...(advertiserInfo ? {
|
|
@@ -819,7 +905,7 @@ export class DatalyrSDKExpo {
|
|
|
819
905
|
// Apple Search Ads attribution
|
|
820
906
|
...asaData,
|
|
821
907
|
},
|
|
822
|
-
|
|
908
|
+
deviceContext,
|
|
823
909
|
source: 'mobile_app',
|
|
824
910
|
timestamp: new Date().toISOString(),
|
|
825
911
|
};
|
package/src/datalyr-sdk.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
getOrCreateVisitorId,
|
|
14
14
|
getOrCreateAnonymousId,
|
|
15
15
|
getOrCreateSessionId,
|
|
16
|
-
|
|
16
|
+
createDeviceContext,
|
|
17
17
|
generateUUID,
|
|
18
18
|
getDeviceInfo,
|
|
19
19
|
getNetworkType,
|
|
@@ -241,7 +241,7 @@ export class DatalyrSDK {
|
|
|
241
241
|
const installData = await attributionManager.trackInstall();
|
|
242
242
|
await this.track('app_install', {
|
|
243
243
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
244
|
-
sdk_version: '1.7.
|
|
244
|
+
sdk_version: '1.7.5',
|
|
245
245
|
...installData,
|
|
246
246
|
});
|
|
247
247
|
}
|
|
@@ -1049,7 +1049,7 @@ export class DatalyrSDK {
|
|
|
1049
1049
|
*/
|
|
1050
1050
|
private async createEventPayload(eventName: string, eventData?: EventData): Promise<EventPayload> {
|
|
1051
1051
|
const deviceInfo = await getDeviceInfo();
|
|
1052
|
-
const
|
|
1052
|
+
const deviceContext = await createDeviceContext();
|
|
1053
1053
|
const attributionData = attributionManager.getAttributionData();
|
|
1054
1054
|
|
|
1055
1055
|
// Get Apple Search Ads attribution if available
|
|
@@ -1097,7 +1097,7 @@ export class DatalyrSDK {
|
|
|
1097
1097
|
carrier: deviceInfo.carrier,
|
|
1098
1098
|
network_type: getNetworkType(),
|
|
1099
1099
|
timestamp: Date.now(),
|
|
1100
|
-
sdk_version: '1.7.
|
|
1100
|
+
sdk_version: '1.7.5',
|
|
1101
1101
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
1102
1102
|
...(advertiserInfo ? {
|
|
1103
1103
|
idfa: advertiserInfo.idfa,
|
|
@@ -1111,7 +1111,7 @@ export class DatalyrSDK {
|
|
|
1111
1111
|
// Apple Search Ads attribution
|
|
1112
1112
|
...asaData,
|
|
1113
1113
|
},
|
|
1114
|
-
|
|
1114
|
+
deviceContext,
|
|
1115
1115
|
source: 'mobile_app',
|
|
1116
1116
|
timestamp: new Date().toISOString(),
|
|
1117
1117
|
};
|
package/src/expo.ts
CHANGED
package/src/http-client.ts
CHANGED
|
@@ -194,11 +194,11 @@ export class HttpClient {
|
|
|
194
194
|
...payload.eventData,
|
|
195
195
|
sessionId: payload.sessionId,
|
|
196
196
|
source: payload.source || 'mobile_app',
|
|
197
|
-
fingerprint: payload.
|
|
197
|
+
fingerprint: payload.deviceContext,
|
|
198
198
|
},
|
|
199
199
|
context: {
|
|
200
200
|
library: '@datalyr/react-native',
|
|
201
|
-
version: '1.
|
|
201
|
+
version: '1.7.5',
|
|
202
202
|
source: 'mobile_app',
|
|
203
203
|
userProperties: payload.userProperties,
|
|
204
204
|
},
|
package/src/types.ts
CHANGED
|
@@ -133,7 +133,7 @@ export interface EventData {
|
|
|
133
133
|
network_type?: string;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
export interface
|
|
136
|
+
export interface DeviceContext {
|
|
137
137
|
deviceId?: string;
|
|
138
138
|
deviceInfo?: {
|
|
139
139
|
model: string;
|
|
@@ -155,7 +155,7 @@ export interface EventPayload {
|
|
|
155
155
|
eventId: string;
|
|
156
156
|
eventName: string;
|
|
157
157
|
eventData?: EventData;
|
|
158
|
-
|
|
158
|
+
deviceContext?: DeviceContext;
|
|
159
159
|
source: 'mobile_app';
|
|
160
160
|
timestamp: string;
|
|
161
161
|
userId?: string;
|
package/src/utils-expo.ts
CHANGED
|
@@ -224,8 +224,8 @@ export const getOrCreateSessionId = async (): Promise<string> => {
|
|
|
224
224
|
}
|
|
225
225
|
};
|
|
226
226
|
|
|
227
|
-
//
|
|
228
|
-
export const
|
|
227
|
+
// Device context creation using Expo APIs
|
|
228
|
+
export const createDeviceContext = async () => {
|
|
229
229
|
try {
|
|
230
230
|
const deviceInfo = await getDeviceInfo();
|
|
231
231
|
|
|
@@ -243,7 +243,7 @@ export const createFingerprintData = async () => {
|
|
|
243
243
|
},
|
|
244
244
|
};
|
|
245
245
|
} catch (error) {
|
|
246
|
-
errorLog('Error creating
|
|
246
|
+
errorLog('Error creating device context:', error as Error);
|
|
247
247
|
const deviceInfo = await getDeviceInfo();
|
|
248
248
|
return {
|
|
249
249
|
deviceId: deviceInfo.deviceId,
|
package/src/utils-interface.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Both utils.ts and utils-expo.ts implement this interface
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { DeviceInfo,
|
|
6
|
+
import { DeviceInfo, DeviceContext } from './types';
|
|
7
7
|
|
|
8
8
|
export interface SDKUtils {
|
|
9
9
|
STORAGE_KEYS: {
|
|
@@ -22,7 +22,7 @@ export interface SDKUtils {
|
|
|
22
22
|
getOrCreateAnonymousId: () => Promise<string>;
|
|
23
23
|
getOrCreateSessionId: () => Promise<string>;
|
|
24
24
|
getDeviceInfo: () => Promise<DeviceInfo>;
|
|
25
|
-
|
|
25
|
+
createDeviceContext: () => Promise<DeviceContext>;
|
|
26
26
|
getNetworkType: () => string | Promise<string>;
|
|
27
27
|
validateEventName: (eventName: string) => boolean;
|
|
28
28
|
validateEventData: (eventData: any) => boolean;
|
package/src/utils.ts
CHANGED
|
@@ -11,7 +11,7 @@ try {
|
|
|
11
11
|
import { v4 as uuidv4 } from 'uuid';
|
|
12
12
|
import 'react-native-get-random-values'; // Required for uuid
|
|
13
13
|
|
|
14
|
-
import { DeviceInfo as DeviceInfoType,
|
|
14
|
+
import { DeviceInfo as DeviceInfoType, DeviceContext } from './types';
|
|
15
15
|
|
|
16
16
|
// Storage Keys
|
|
17
17
|
export const STORAGE_KEYS = {
|
|
@@ -250,9 +250,9 @@ const fetchDeviceInfoInternal = async (): Promise<DeviceInfoType> => {
|
|
|
250
250
|
};
|
|
251
251
|
|
|
252
252
|
/**
|
|
253
|
-
* Create
|
|
253
|
+
* Create device context for attribution
|
|
254
254
|
*/
|
|
255
|
-
export const
|
|
255
|
+
export const createDeviceContext = async (): Promise<DeviceContext> => {
|
|
256
256
|
const deviceInfo = await getDeviceInfo();
|
|
257
257
|
|
|
258
258
|
return {
|