@datalyr/react-native 1.0.5 → 1.1.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.
- package/README.md +30 -0
- package/lib/datalyr-sdk.d.ts +7 -0
- package/lib/datalyr-sdk.js +22 -4
- package/lib/http-client.js +1 -0
- package/lib/types.d.ts +2 -0
- package/lib/utils.d.ts +6 -0
- package/lib/utils.js +20 -0
- package/package.json +1 -1
- package/src/datalyr-sdk.ts +25 -3
- package/src/http-client.ts +1 -0
- package/src/types.ts +2 -0
- package/src/utils.ts +20 -0
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Official Datalyr SDK for React Native & Expo - Mobile attribution tracking and a
|
|
|
14
14
|
- 💾 **Offline Support** - Events saved and retried when reconnected
|
|
15
15
|
- 🔒 **Privacy First** - GDPR/CCPA compliant
|
|
16
16
|
- ⚡ **Lightweight** - < 100KB, minimal battery impact
|
|
17
|
+
- 🆔 **Identity Resolution** - Persistent anonymous ID links web → mobile → server events
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
@@ -164,6 +165,35 @@ await Datalyr.setAttributionData({
|
|
|
164
165
|
});
|
|
165
166
|
```
|
|
166
167
|
|
|
168
|
+
## Identity Resolution (New in v1.1.0)
|
|
169
|
+
|
|
170
|
+
The SDK now includes persistent anonymous IDs for complete user journey tracking:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Get anonymous ID (persists across app sessions)
|
|
174
|
+
const anonymousId = Datalyr.getAnonymousId();
|
|
175
|
+
|
|
176
|
+
// Pass to your backend for attribution preservation
|
|
177
|
+
await fetch('/api/purchase', {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
items: cart,
|
|
181
|
+
anonymous_id: anonymousId // Links server events to mobile events
|
|
182
|
+
})
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Identity is automatically linked when you identify a user
|
|
186
|
+
await Datalyr.identify('user_123', {
|
|
187
|
+
email: 'user@example.com'
|
|
188
|
+
});
|
|
189
|
+
// This creates a $identify event that links anonymous_id to user_id
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Key Benefits:
|
|
193
|
+
- **Attribution Preservation**: Never lose fbclid, gclid, ttclid, or lyr tracking
|
|
194
|
+
- **Complete Journey**: Track users from web → app → server
|
|
195
|
+
- **Automatic Linking**: Identity resolution happens automatically
|
|
196
|
+
|
|
167
197
|
## Session Management
|
|
168
198
|
|
|
169
199
|
Sessions are tracked automatically with a 30-minute timeout.
|
package/lib/datalyr-sdk.d.ts
CHANGED
|
@@ -45,11 +45,16 @@ export declare class DatalyrSDK {
|
|
|
45
45
|
initialized: boolean;
|
|
46
46
|
workspaceId: string;
|
|
47
47
|
visitorId: string;
|
|
48
|
+
anonymousId: string;
|
|
48
49
|
sessionId: string;
|
|
49
50
|
currentUserId?: string;
|
|
50
51
|
queueStats: any;
|
|
51
52
|
attribution: any;
|
|
52
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Get the persistent anonymous ID
|
|
56
|
+
*/
|
|
57
|
+
getAnonymousId(): string;
|
|
53
58
|
/**
|
|
54
59
|
* Get detailed attribution data
|
|
55
60
|
*/
|
|
@@ -151,11 +156,13 @@ export declare class Datalyr {
|
|
|
151
156
|
initialized: boolean;
|
|
152
157
|
workspaceId: string;
|
|
153
158
|
visitorId: string;
|
|
159
|
+
anonymousId: string;
|
|
154
160
|
sessionId: string;
|
|
155
161
|
currentUserId?: string;
|
|
156
162
|
queueStats: any;
|
|
157
163
|
attribution: any;
|
|
158
164
|
};
|
|
165
|
+
static getAnonymousId(): string;
|
|
159
166
|
static getAttributionData(): AttributionData;
|
|
160
167
|
static setAttributionData(data: Partial<AttributionData>): Promise<void>;
|
|
161
168
|
static getCurrentSession(): SessionData | null;
|
package/lib/datalyr-sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Platform, AppState } from 'react-native';
|
|
2
|
-
import { getOrCreateVisitorId, getOrCreateSessionId, createFingerprintData, generateUUID, getDeviceInfo, getNetworkType, validateEventName, validateEventData, debugLog, errorLog, Storage, STORAGE_KEYS, } from './utils';
|
|
2
|
+
import { getOrCreateVisitorId, getOrCreateAnonymousId, getOrCreateSessionId, createFingerprintData, 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';
|
|
@@ -27,6 +27,7 @@ export class DatalyrSDK {
|
|
|
27
27
|
respectDoNotTrack: true,
|
|
28
28
|
},
|
|
29
29
|
visitorId: '',
|
|
30
|
+
anonymousId: '', // Persistent anonymous identifier
|
|
30
31
|
sessionId: '',
|
|
31
32
|
userProperties: {},
|
|
32
33
|
eventQueue: [],
|
|
@@ -70,8 +71,9 @@ export class DatalyrSDK {
|
|
|
70
71
|
flushInterval: this.state.config.flushInterval || 30000,
|
|
71
72
|
maxRetryCount: this.state.config.maxRetries || 3,
|
|
72
73
|
});
|
|
73
|
-
// Initialize visitor ID and session
|
|
74
|
+
// Initialize visitor ID, anonymous ID and session
|
|
74
75
|
this.state.visitorId = await getOrCreateVisitorId();
|
|
76
|
+
this.state.anonymousId = await getOrCreateAnonymousId();
|
|
75
77
|
this.state.sessionId = await getOrCreateSessionId();
|
|
76
78
|
// Load persisted user data
|
|
77
79
|
await this.loadPersistedUserData();
|
|
@@ -128,6 +130,7 @@ export class DatalyrSDK {
|
|
|
128
130
|
debugLog('Datalyr SDK initialized successfully', {
|
|
129
131
|
workspaceId: this.state.config.workspaceId,
|
|
130
132
|
visitorId: this.state.visitorId,
|
|
133
|
+
anonymousId: this.state.anonymousId,
|
|
131
134
|
sessionId: this.state.sessionId,
|
|
132
135
|
});
|
|
133
136
|
}
|
|
@@ -191,9 +194,10 @@ export class DatalyrSDK {
|
|
|
191
194
|
this.state.userProperties = { ...this.state.userProperties, ...properties };
|
|
192
195
|
// Persist user data
|
|
193
196
|
await this.persistUserData();
|
|
194
|
-
// Track identify event
|
|
195
|
-
await this.track('identify', {
|
|
197
|
+
// Track $identify event for identity resolution
|
|
198
|
+
await this.track('$identify', {
|
|
196
199
|
userId,
|
|
200
|
+
anonymous_id: this.state.anonymousId,
|
|
197
201
|
...properties
|
|
198
202
|
});
|
|
199
203
|
}
|
|
@@ -214,6 +218,7 @@ export class DatalyrSDK {
|
|
|
214
218
|
newUserId,
|
|
215
219
|
previousId: previousId || this.state.visitorId,
|
|
216
220
|
visitorId: this.state.visitorId,
|
|
221
|
+
anonymousId: this.state.anonymousId, // Include for identity resolution
|
|
217
222
|
};
|
|
218
223
|
debugLog('Aliasing user:', aliasData);
|
|
219
224
|
// Track alias event
|
|
@@ -265,12 +270,19 @@ export class DatalyrSDK {
|
|
|
265
270
|
initialized: this.state.initialized,
|
|
266
271
|
workspaceId: this.state.config.workspaceId || '',
|
|
267
272
|
visitorId: this.state.visitorId,
|
|
273
|
+
anonymousId: this.state.anonymousId,
|
|
268
274
|
sessionId: this.state.sessionId,
|
|
269
275
|
currentUserId: this.state.currentUserId,
|
|
270
276
|
queueStats: this.eventQueue.getStats(),
|
|
271
277
|
attribution: attributionManager.getAttributionSummary(),
|
|
272
278
|
};
|
|
273
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Get the persistent anonymous ID
|
|
282
|
+
*/
|
|
283
|
+
getAnonymousId() {
|
|
284
|
+
return this.state.anonymousId;
|
|
285
|
+
}
|
|
274
286
|
/**
|
|
275
287
|
* Get detailed attribution data
|
|
276
288
|
*/
|
|
@@ -383,11 +395,14 @@ export class DatalyrSDK {
|
|
|
383
395
|
const payload = {
|
|
384
396
|
workspaceId: this.state.config.workspaceId || 'mobile_sdk',
|
|
385
397
|
visitorId: this.state.visitorId,
|
|
398
|
+
anonymousId: this.state.anonymousId, // Include persistent anonymous ID
|
|
386
399
|
sessionId: this.state.sessionId,
|
|
387
400
|
eventId: generateUUID(),
|
|
388
401
|
eventName,
|
|
389
402
|
eventData: {
|
|
390
403
|
...eventData,
|
|
404
|
+
// Include anonymous_id in event data for attribution
|
|
405
|
+
anonymous_id: this.state.anonymousId,
|
|
391
406
|
// Auto-captured mobile data
|
|
392
407
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
393
408
|
os_version: deviceInfo.osVersion,
|
|
@@ -578,6 +593,9 @@ export class Datalyr {
|
|
|
578
593
|
static getStatus() {
|
|
579
594
|
return datalyr.getStatus();
|
|
580
595
|
}
|
|
596
|
+
static getAnonymousId() {
|
|
597
|
+
return datalyr.getAnonymousId();
|
|
598
|
+
}
|
|
581
599
|
static getAttributionData() {
|
|
582
600
|
return datalyr.getAttributionData();
|
|
583
601
|
}
|
package/lib/http-client.js
CHANGED
package/lib/types.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ export interface FingerprintData {
|
|
|
59
59
|
export interface EventPayload {
|
|
60
60
|
workspaceId: string;
|
|
61
61
|
visitorId: string;
|
|
62
|
+
anonymousId: string;
|
|
62
63
|
sessionId: string;
|
|
63
64
|
eventId: string;
|
|
64
65
|
eventName: string;
|
|
@@ -81,6 +82,7 @@ export interface SDKState {
|
|
|
81
82
|
initialized: boolean;
|
|
82
83
|
config: DatalyrConfig;
|
|
83
84
|
visitorId: string;
|
|
85
|
+
anonymousId: string;
|
|
84
86
|
sessionId: string;
|
|
85
87
|
currentUserId?: string;
|
|
86
88
|
userProperties: UserProperties;
|
package/lib/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import 'react-native-get-random-values';
|
|
|
2
2
|
import { DeviceInfo as DeviceInfoType, FingerprintData } from './types';
|
|
3
3
|
export declare const STORAGE_KEYS: {
|
|
4
4
|
VISITOR_ID: string;
|
|
5
|
+
ANONYMOUS_ID: string;
|
|
5
6
|
SESSION_ID: string;
|
|
6
7
|
USER_ID: string;
|
|
7
8
|
USER_PROPERTIES: string;
|
|
@@ -25,6 +26,11 @@ export declare const hashString: (str: string) => string;
|
|
|
25
26
|
* Get or create a persistent visitor ID
|
|
26
27
|
*/
|
|
27
28
|
export declare const getOrCreateVisitorId: () => Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Get or create a persistent anonymous ID
|
|
31
|
+
* This ID persists across app reinstalls and never changes
|
|
32
|
+
*/
|
|
33
|
+
export declare const getOrCreateAnonymousId: () => Promise<string>;
|
|
28
34
|
/**
|
|
29
35
|
* Get or create a session ID (with session timeout logic)
|
|
30
36
|
*/
|
package/lib/utils.js
CHANGED
|
@@ -13,6 +13,7 @@ import 'react-native-get-random-values'; // Required for uuid
|
|
|
13
13
|
// Storage Keys
|
|
14
14
|
export const STORAGE_KEYS = {
|
|
15
15
|
VISITOR_ID: '@datalyr/visitor_id',
|
|
16
|
+
ANONYMOUS_ID: '@datalyr/anonymous_id', // Persistent anonymous identifier
|
|
16
17
|
SESSION_ID: '@datalyr/session_id',
|
|
17
18
|
USER_ID: '@datalyr/user_id',
|
|
18
19
|
USER_PROPERTIES: '@datalyr/user_properties',
|
|
@@ -63,6 +64,25 @@ export const getOrCreateVisitorId = async () => {
|
|
|
63
64
|
return generateUUID(); // Fallback to memory-only ID
|
|
64
65
|
}
|
|
65
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Get or create a persistent anonymous ID
|
|
69
|
+
* This ID persists across app reinstalls and never changes
|
|
70
|
+
*/
|
|
71
|
+
export const getOrCreateAnonymousId = async () => {
|
|
72
|
+
try {
|
|
73
|
+
let anonymousId = await AsyncStorage.getItem(STORAGE_KEYS.ANONYMOUS_ID);
|
|
74
|
+
if (!anonymousId) {
|
|
75
|
+
// Generate anonymous_id with anon_ prefix to match web SDK
|
|
76
|
+
anonymousId = `anon_${generateUUID()}`;
|
|
77
|
+
await AsyncStorage.setItem(STORAGE_KEYS.ANONYMOUS_ID, anonymousId);
|
|
78
|
+
}
|
|
79
|
+
return anonymousId;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.warn('Failed to get/create anonymous ID:', error);
|
|
83
|
+
return `anon_${generateUUID()}`; // Fallback to memory-only ID
|
|
84
|
+
}
|
|
85
|
+
};
|
|
66
86
|
/**
|
|
67
87
|
* Get or create a session ID (with session timeout logic)
|
|
68
88
|
*/
|
package/package.json
CHANGED
package/src/datalyr-sdk.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from './types';
|
|
11
11
|
import {
|
|
12
12
|
getOrCreateVisitorId,
|
|
13
|
+
getOrCreateAnonymousId,
|
|
13
14
|
getOrCreateSessionId,
|
|
14
15
|
createFingerprintData,
|
|
15
16
|
generateUUID,
|
|
@@ -56,6 +57,7 @@ export class DatalyrSDK {
|
|
|
56
57
|
respectDoNotTrack: true,
|
|
57
58
|
},
|
|
58
59
|
visitorId: '',
|
|
60
|
+
anonymousId: '', // Persistent anonymous identifier
|
|
59
61
|
sessionId: '',
|
|
60
62
|
userProperties: {},
|
|
61
63
|
eventQueue: [],
|
|
@@ -106,8 +108,9 @@ export class DatalyrSDK {
|
|
|
106
108
|
maxRetryCount: this.state.config.maxRetries || 3,
|
|
107
109
|
});
|
|
108
110
|
|
|
109
|
-
// Initialize visitor ID and session
|
|
111
|
+
// Initialize visitor ID, anonymous ID and session
|
|
110
112
|
this.state.visitorId = await getOrCreateVisitorId();
|
|
113
|
+
this.state.anonymousId = await getOrCreateAnonymousId();
|
|
111
114
|
this.state.sessionId = await getOrCreateSessionId();
|
|
112
115
|
|
|
113
116
|
// Load persisted user data
|
|
@@ -174,6 +177,7 @@ export class DatalyrSDK {
|
|
|
174
177
|
debugLog('Datalyr SDK initialized successfully', {
|
|
175
178
|
workspaceId: this.state.config.workspaceId,
|
|
176
179
|
visitorId: this.state.visitorId,
|
|
180
|
+
anonymousId: this.state.anonymousId,
|
|
177
181
|
sessionId: this.state.sessionId,
|
|
178
182
|
});
|
|
179
183
|
|
|
@@ -251,9 +255,10 @@ export class DatalyrSDK {
|
|
|
251
255
|
// Persist user data
|
|
252
256
|
await this.persistUserData();
|
|
253
257
|
|
|
254
|
-
// Track identify event
|
|
255
|
-
await this.track('identify', {
|
|
258
|
+
// Track $identify event for identity resolution
|
|
259
|
+
await this.track('$identify', {
|
|
256
260
|
userId,
|
|
261
|
+
anonymous_id: this.state.anonymousId,
|
|
257
262
|
...properties
|
|
258
263
|
});
|
|
259
264
|
|
|
@@ -276,6 +281,7 @@ export class DatalyrSDK {
|
|
|
276
281
|
newUserId,
|
|
277
282
|
previousId: previousId || this.state.visitorId,
|
|
278
283
|
visitorId: this.state.visitorId,
|
|
284
|
+
anonymousId: this.state.anonymousId, // Include for identity resolution
|
|
279
285
|
};
|
|
280
286
|
|
|
281
287
|
debugLog('Aliasing user:', aliasData);
|
|
@@ -335,6 +341,7 @@ export class DatalyrSDK {
|
|
|
335
341
|
initialized: boolean;
|
|
336
342
|
workspaceId: string;
|
|
337
343
|
visitorId: string;
|
|
344
|
+
anonymousId: string;
|
|
338
345
|
sessionId: string;
|
|
339
346
|
currentUserId?: string;
|
|
340
347
|
queueStats: any;
|
|
@@ -344,6 +351,7 @@ export class DatalyrSDK {
|
|
|
344
351
|
initialized: this.state.initialized,
|
|
345
352
|
workspaceId: this.state.config.workspaceId || '',
|
|
346
353
|
visitorId: this.state.visitorId,
|
|
354
|
+
anonymousId: this.state.anonymousId,
|
|
347
355
|
sessionId: this.state.sessionId,
|
|
348
356
|
currentUserId: this.state.currentUserId,
|
|
349
357
|
queueStats: this.eventQueue.getStats(),
|
|
@@ -351,6 +359,13 @@ export class DatalyrSDK {
|
|
|
351
359
|
};
|
|
352
360
|
}
|
|
353
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Get the persistent anonymous ID
|
|
364
|
+
*/
|
|
365
|
+
getAnonymousId(): string {
|
|
366
|
+
return this.state.anonymousId;
|
|
367
|
+
}
|
|
368
|
+
|
|
354
369
|
/**
|
|
355
370
|
* Get detailed attribution data
|
|
356
371
|
*/
|
|
@@ -489,11 +504,14 @@ export class DatalyrSDK {
|
|
|
489
504
|
const payload: EventPayload = {
|
|
490
505
|
workspaceId: this.state.config.workspaceId || 'mobile_sdk',
|
|
491
506
|
visitorId: this.state.visitorId,
|
|
507
|
+
anonymousId: this.state.anonymousId, // Include persistent anonymous ID
|
|
492
508
|
sessionId: this.state.sessionId,
|
|
493
509
|
eventId: generateUUID(),
|
|
494
510
|
eventName,
|
|
495
511
|
eventData: {
|
|
496
512
|
...eventData,
|
|
513
|
+
// Include anonymous_id in event data for attribution
|
|
514
|
+
anonymous_id: this.state.anonymousId,
|
|
497
515
|
// Auto-captured mobile data
|
|
498
516
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
499
517
|
os_version: deviceInfo.osVersion,
|
|
@@ -721,6 +739,10 @@ export class Datalyr {
|
|
|
721
739
|
return datalyr.getStatus();
|
|
722
740
|
}
|
|
723
741
|
|
|
742
|
+
static getAnonymousId(): string {
|
|
743
|
+
return datalyr.getAnonymousId();
|
|
744
|
+
}
|
|
745
|
+
|
|
724
746
|
static getAttributionData(): AttributionData {
|
|
725
747
|
return datalyr.getAttributionData();
|
|
726
748
|
}
|
package/src/http-client.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -66,6 +66,7 @@ export interface FingerprintData {
|
|
|
66
66
|
export interface EventPayload {
|
|
67
67
|
workspaceId: string;
|
|
68
68
|
visitorId: string;
|
|
69
|
+
anonymousId: string; // Persistent anonymous identifier
|
|
69
70
|
sessionId: string;
|
|
70
71
|
eventId: string;
|
|
71
72
|
eventName: string;
|
|
@@ -92,6 +93,7 @@ export interface SDKState {
|
|
|
92
93
|
initialized: boolean;
|
|
93
94
|
config: DatalyrConfig;
|
|
94
95
|
visitorId: string;
|
|
96
|
+
anonymousId: string; // Persistent anonymous identifier
|
|
95
97
|
sessionId: string;
|
|
96
98
|
currentUserId?: string;
|
|
97
99
|
userProperties: UserProperties;
|
package/src/utils.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { DeviceInfo as DeviceInfoType, FingerprintData } from './types';
|
|
|
16
16
|
// Storage Keys
|
|
17
17
|
export const STORAGE_KEYS = {
|
|
18
18
|
VISITOR_ID: '@datalyr/visitor_id',
|
|
19
|
+
ANONYMOUS_ID: '@datalyr/anonymous_id', // Persistent anonymous identifier
|
|
19
20
|
SESSION_ID: '@datalyr/session_id',
|
|
20
21
|
USER_ID: '@datalyr/user_id',
|
|
21
22
|
USER_PROPERTIES: '@datalyr/user_properties',
|
|
@@ -71,6 +72,25 @@ export const getOrCreateVisitorId = async (): Promise<string> => {
|
|
|
71
72
|
}
|
|
72
73
|
};
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Get or create a persistent anonymous ID
|
|
77
|
+
* This ID persists across app reinstalls and never changes
|
|
78
|
+
*/
|
|
79
|
+
export const getOrCreateAnonymousId = async (): Promise<string> => {
|
|
80
|
+
try {
|
|
81
|
+
let anonymousId = await AsyncStorage.getItem(STORAGE_KEYS.ANONYMOUS_ID);
|
|
82
|
+
if (!anonymousId) {
|
|
83
|
+
// Generate anonymous_id with anon_ prefix to match web SDK
|
|
84
|
+
anonymousId = `anon_${generateUUID()}`;
|
|
85
|
+
await AsyncStorage.setItem(STORAGE_KEYS.ANONYMOUS_ID, anonymousId);
|
|
86
|
+
}
|
|
87
|
+
return anonymousId;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn('Failed to get/create anonymous ID:', error);
|
|
90
|
+
return `anon_${generateUUID()}`; // Fallback to memory-only ID
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
74
94
|
/**
|
|
75
95
|
* Get or create a session ID (with session timeout logic)
|
|
76
96
|
*/
|