@datalyr/react-native 1.2.1 → 1.3.1
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/CHANGELOG.md +19 -0
- package/README.md +145 -9
- package/android/build.gradle +54 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrNativeModule.java +423 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPackage.java +30 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPlayInstallReferrerModule.java +229 -0
- package/datalyr-react-native.podspec +2 -2
- package/ios/DatalyrSKAdNetwork.m +400 -1
- package/ios/PrivacyInfo.xcprivacy +48 -0
- package/lib/ConversionValueEncoder.d.ts +13 -1
- package/lib/ConversionValueEncoder.js +57 -23
- package/lib/datalyr-sdk.d.ts +31 -2
- package/lib/datalyr-sdk.js +138 -30
- package/lib/index.d.ts +5 -1
- package/lib/index.js +4 -1
- package/lib/integrations/index.d.ts +3 -1
- package/lib/integrations/index.js +2 -1
- package/lib/integrations/meta-integration.d.ts +1 -0
- package/lib/integrations/meta-integration.js +4 -3
- package/lib/integrations/play-install-referrer.d.ts +78 -0
- package/lib/integrations/play-install-referrer.js +166 -0
- package/lib/integrations/tiktok-integration.d.ts +1 -0
- package/lib/integrations/tiktok-integration.js +4 -3
- package/lib/journey.d.ts +106 -0
- package/lib/journey.js +258 -0
- package/lib/native/DatalyrNativeBridge.d.ts +42 -3
- package/lib/native/DatalyrNativeBridge.js +63 -9
- package/lib/native/SKAdNetworkBridge.d.ts +142 -0
- package/lib/native/SKAdNetworkBridge.js +328 -0
- package/lib/network-status.d.ts +84 -0
- package/lib/network-status.js +281 -0
- package/lib/types.d.ts +51 -0
- package/lib/utils.d.ts +6 -1
- package/lib/utils.js +52 -2
- package/package.json +13 -4
- package/src/ConversionValueEncoder.ts +67 -26
- package/src/datalyr-sdk-expo.ts +55 -6
- package/src/datalyr-sdk.ts +161 -38
- package/src/expo.ts +4 -0
- package/src/index.ts +7 -1
- package/src/integrations/index.ts +3 -1
- package/src/integrations/meta-integration.ts +4 -3
- package/src/integrations/play-install-referrer.ts +218 -0
- package/src/integrations/tiktok-integration.ts +4 -3
- package/src/journey.ts +338 -0
- package/src/native/DatalyrNativeBridge.ts +99 -13
- package/src/native/SKAdNetworkBridge.ts +481 -2
- package/src/network-status.ts +312 -0
- package/src/types.ts +74 -6
- package/src/utils.ts +62 -6
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>NSPrivacyTracking</key>
|
|
6
|
+
<false/>
|
|
7
|
+
<key>NSPrivacyTrackingDomains</key>
|
|
8
|
+
<array/>
|
|
9
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
10
|
+
<array>
|
|
11
|
+
<dict>
|
|
12
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
13
|
+
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
|
14
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
15
|
+
<false/>
|
|
16
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
17
|
+
<false/>
|
|
18
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
19
|
+
<array>
|
|
20
|
+
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
21
|
+
</array>
|
|
22
|
+
</dict>
|
|
23
|
+
<dict>
|
|
24
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
25
|
+
<string>NSPrivacyCollectedDataTypeProductInteraction</string>
|
|
26
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
27
|
+
<false/>
|
|
28
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
29
|
+
<false/>
|
|
30
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
31
|
+
<array>
|
|
32
|
+
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
33
|
+
</array>
|
|
34
|
+
</dict>
|
|
35
|
+
</array>
|
|
36
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
37
|
+
<array>
|
|
38
|
+
<dict>
|
|
39
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
40
|
+
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
41
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
42
|
+
<array>
|
|
43
|
+
<string>CA92.1</string>
|
|
44
|
+
</array>
|
|
45
|
+
</dict>
|
|
46
|
+
</array>
|
|
47
|
+
</dict>
|
|
48
|
+
</plist>
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { SKANCoarseValue, SKANConversionResult } from './native/SKAdNetworkBridge';
|
|
1
2
|
interface EventMapping {
|
|
2
3
|
bits: number[];
|
|
3
4
|
revenueBits?: number[];
|
|
4
5
|
priority: number;
|
|
6
|
+
coarseValue?: SKANCoarseValue;
|
|
7
|
+
lockWindow?: boolean;
|
|
5
8
|
}
|
|
6
9
|
interface ConversionTemplate {
|
|
7
10
|
name: string;
|
|
@@ -11,13 +14,22 @@ export declare class ConversionValueEncoder {
|
|
|
11
14
|
private template;
|
|
12
15
|
constructor(template: ConversionTemplate);
|
|
13
16
|
/**
|
|
14
|
-
* Encode an event into Apple's 0-63 conversion value format
|
|
17
|
+
* Encode an event into Apple's 0-63 conversion value format (SKAN 3.0 compatible)
|
|
18
|
+
* @deprecated Use encodeWithSKAN4 for iOS 16.1+
|
|
15
19
|
*/
|
|
16
20
|
encode(event: string, properties?: Record<string, any>): number;
|
|
21
|
+
/**
|
|
22
|
+
* Encode an event with full SKAN 4.0 support (fine value, coarse value, lock window)
|
|
23
|
+
*/
|
|
24
|
+
encodeWithSKAN4(event: string, properties?: Record<string, any>): SKANConversionResult;
|
|
17
25
|
/**
|
|
18
26
|
* Map revenue amount to 3-bit tier (0-7)
|
|
19
27
|
*/
|
|
20
28
|
private getRevenueTier;
|
|
29
|
+
/**
|
|
30
|
+
* Map revenue to SKAN 4.0 coarse value
|
|
31
|
+
*/
|
|
32
|
+
private getCoarseValueForRevenue;
|
|
21
33
|
}
|
|
22
34
|
export declare const ConversionTemplates: {
|
|
23
35
|
ecommerce: ConversionTemplate;
|
|
@@ -3,18 +3,27 @@ export class ConversionValueEncoder {
|
|
|
3
3
|
this.template = template;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Encode an event into Apple's 0-63 conversion value format
|
|
6
|
+
* Encode an event into Apple's 0-63 conversion value format (SKAN 3.0 compatible)
|
|
7
|
+
* @deprecated Use encodeWithSKAN4 for iOS 16.1+
|
|
7
8
|
*/
|
|
8
9
|
encode(event, properties) {
|
|
10
|
+
return this.encodeWithSKAN4(event, properties).fineValue;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Encode an event with full SKAN 4.0 support (fine value, coarse value, lock window)
|
|
14
|
+
*/
|
|
15
|
+
encodeWithSKAN4(event, properties) {
|
|
9
16
|
const mapping = this.template.events[event];
|
|
10
|
-
if (!mapping)
|
|
11
|
-
return 0;
|
|
17
|
+
if (!mapping) {
|
|
18
|
+
return { fineValue: 0, coarseValue: 'low', lockWindow: false, priority: 0 };
|
|
19
|
+
}
|
|
12
20
|
let conversionValue = 0;
|
|
13
21
|
// Set event bits
|
|
14
22
|
for (const bit of mapping.bits) {
|
|
15
23
|
conversionValue |= (1 << bit);
|
|
16
24
|
}
|
|
17
25
|
// Set revenue bits if revenue is provided
|
|
26
|
+
let coarseValue = mapping.coarseValue || 'medium';
|
|
18
27
|
if (mapping.revenueBits && properties) {
|
|
19
28
|
const revenue = properties.revenue || properties.value || 0;
|
|
20
29
|
const revenueTier = this.getRevenueTier(revenue);
|
|
@@ -23,8 +32,17 @@ export class ConversionValueEncoder {
|
|
|
23
32
|
conversionValue |= (1 << mapping.revenueBits[i]);
|
|
24
33
|
}
|
|
25
34
|
}
|
|
35
|
+
// Upgrade coarse value based on revenue
|
|
36
|
+
coarseValue = this.getCoarseValueForRevenue(revenue);
|
|
26
37
|
}
|
|
27
|
-
|
|
38
|
+
// Ensure value is within 0-63 range
|
|
39
|
+
const fineValue = Math.min(conversionValue, 63);
|
|
40
|
+
return {
|
|
41
|
+
fineValue,
|
|
42
|
+
coarseValue,
|
|
43
|
+
lockWindow: mapping.lockWindow || false,
|
|
44
|
+
priority: mapping.priority
|
|
45
|
+
};
|
|
28
46
|
}
|
|
29
47
|
/**
|
|
30
48
|
* Map revenue amount to 3-bit tier (0-7)
|
|
@@ -46,40 +64,56 @@ export class ConversionValueEncoder {
|
|
|
46
64
|
return 6; // $100-250
|
|
47
65
|
return 7; // $250+
|
|
48
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Map revenue to SKAN 4.0 coarse value
|
|
69
|
+
*/
|
|
70
|
+
getCoarseValueForRevenue(revenue) {
|
|
71
|
+
if (revenue < 10)
|
|
72
|
+
return 'low'; // $0-10 = low value
|
|
73
|
+
if (revenue < 50)
|
|
74
|
+
return 'medium'; // $10-50 = medium value
|
|
75
|
+
return 'high'; // $50+ = high value
|
|
76
|
+
}
|
|
49
77
|
}
|
|
50
|
-
// Industry templates
|
|
78
|
+
// Industry templates with SKAN 4.0 support
|
|
51
79
|
export const ConversionTemplates = {
|
|
80
|
+
// E-commerce template - optimized for online stores
|
|
81
|
+
// SKAN 4.0: purchase locks window, high-value events get "high" coarse value
|
|
52
82
|
ecommerce: {
|
|
53
83
|
name: 'ecommerce',
|
|
54
84
|
events: {
|
|
55
|
-
purchase: { bits: [0], revenueBits: [1, 2, 3], priority: 100 },
|
|
56
|
-
add_to_cart: { bits: [4], priority: 30 },
|
|
57
|
-
begin_checkout: { bits: [5], priority: 50 },
|
|
58
|
-
signup: { bits: [6], priority: 20 },
|
|
59
|
-
subscribe: { bits: [0, 1], revenueBits: [2, 3, 4], priority: 90 },
|
|
60
|
-
view_item: { bits: [7], priority: 10 }
|
|
85
|
+
purchase: { bits: [0], revenueBits: [1, 2, 3], priority: 100, coarseValue: 'high', lockWindow: true },
|
|
86
|
+
add_to_cart: { bits: [4], priority: 30, coarseValue: 'low' },
|
|
87
|
+
begin_checkout: { bits: [5], priority: 50, coarseValue: 'medium' },
|
|
88
|
+
signup: { bits: [6], priority: 20, coarseValue: 'low' },
|
|
89
|
+
subscribe: { bits: [0, 1], revenueBits: [2, 3, 4], priority: 90, coarseValue: 'high', lockWindow: true },
|
|
90
|
+
view_item: { bits: [7], priority: 10, coarseValue: 'low' }
|
|
61
91
|
}
|
|
62
92
|
},
|
|
93
|
+
// Gaming template - optimized for mobile games
|
|
94
|
+
// SKAN 4.0: purchase locks window, tutorial completion is medium value
|
|
63
95
|
gaming: {
|
|
64
96
|
name: 'gaming',
|
|
65
97
|
events: {
|
|
66
|
-
level_complete: { bits: [0], priority: 40 },
|
|
67
|
-
tutorial_complete: { bits: [1], priority: 60 },
|
|
68
|
-
purchase: { bits: [2], revenueBits: [3, 4, 5], priority: 100 },
|
|
69
|
-
achievement_unlocked: { bits: [6], priority: 30 },
|
|
70
|
-
session_start: { bits: [7], priority: 10 },
|
|
71
|
-
ad_watched: { bits: [0, 6], priority: 20 }
|
|
98
|
+
level_complete: { bits: [0], priority: 40, coarseValue: 'medium' },
|
|
99
|
+
tutorial_complete: { bits: [1], priority: 60, coarseValue: 'medium' },
|
|
100
|
+
purchase: { bits: [2], revenueBits: [3, 4, 5], priority: 100, coarseValue: 'high', lockWindow: true },
|
|
101
|
+
achievement_unlocked: { bits: [6], priority: 30, coarseValue: 'low' },
|
|
102
|
+
session_start: { bits: [7], priority: 10, coarseValue: 'low' },
|
|
103
|
+
ad_watched: { bits: [0, 6], priority: 20, coarseValue: 'low' }
|
|
72
104
|
}
|
|
73
105
|
},
|
|
106
|
+
// Subscription template - optimized for subscription apps
|
|
107
|
+
// SKAN 4.0: subscribe/upgrade lock window, trial is medium value
|
|
74
108
|
subscription: {
|
|
75
109
|
name: 'subscription',
|
|
76
110
|
events: {
|
|
77
|
-
trial_start: { bits: [0], priority: 70 },
|
|
78
|
-
subscribe: { bits: [1], revenueBits: [2, 3, 4], priority: 100 },
|
|
79
|
-
upgrade: { bits: [1, 5], revenueBits: [2, 3, 4], priority: 90 },
|
|
80
|
-
cancel: { bits: [6], priority: 20 },
|
|
81
|
-
signup: { bits: [7], priority: 30 },
|
|
82
|
-
payment_method_added: { bits: [0, 7], priority: 50 }
|
|
111
|
+
trial_start: { bits: [0], priority: 70, coarseValue: 'medium' },
|
|
112
|
+
subscribe: { bits: [1], revenueBits: [2, 3, 4], priority: 100, coarseValue: 'high', lockWindow: true },
|
|
113
|
+
upgrade: { bits: [1, 5], revenueBits: [2, 3, 4], priority: 90, coarseValue: 'high', lockWindow: true },
|
|
114
|
+
cancel: { bits: [6], priority: 20, coarseValue: 'low' },
|
|
115
|
+
signup: { bits: [7], priority: 30, coarseValue: 'low' },
|
|
116
|
+
payment_method_added: { bits: [0, 7], priority: 50, coarseValue: 'medium' }
|
|
83
117
|
}
|
|
84
118
|
}
|
|
85
119
|
};
|
package/lib/datalyr-sdk.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare class DatalyrSDK {
|
|
|
8
8
|
private eventQueue;
|
|
9
9
|
private autoEventsManager;
|
|
10
10
|
private appStateSubscription;
|
|
11
|
+
private networkStatusUnsubscribe;
|
|
11
12
|
private static conversionEncoder?;
|
|
12
13
|
private static debugEnabled;
|
|
13
14
|
constructor();
|
|
@@ -56,15 +57,30 @@ export declare class DatalyrSDK {
|
|
|
56
57
|
currentUserId?: string;
|
|
57
58
|
queueStats: any;
|
|
58
59
|
attribution: any;
|
|
60
|
+
journey: any;
|
|
59
61
|
};
|
|
60
62
|
/**
|
|
61
63
|
* Get the persistent anonymous ID
|
|
62
64
|
*/
|
|
63
65
|
getAnonymousId(): string;
|
|
64
66
|
/**
|
|
65
|
-
* Get detailed attribution data
|
|
67
|
+
* Get detailed attribution data (includes journey tracking data)
|
|
66
68
|
*/
|
|
67
|
-
getAttributionData(): AttributionData
|
|
69
|
+
getAttributionData(): AttributionData & Record<string, any>;
|
|
70
|
+
/**
|
|
71
|
+
* Get journey tracking summary
|
|
72
|
+
*/
|
|
73
|
+
getJourneySummary(): {
|
|
74
|
+
hasFirstTouch: boolean;
|
|
75
|
+
hasLastTouch: boolean;
|
|
76
|
+
touchpointCount: number;
|
|
77
|
+
daysSinceFirstTouch: number;
|
|
78
|
+
sources: string[];
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Get full customer journey (all touchpoints)
|
|
82
|
+
*/
|
|
83
|
+
getJourney(): import("./journey").TouchPoint[];
|
|
68
84
|
/**
|
|
69
85
|
* Set custom attribution data (for testing or manual attribution)
|
|
70
86
|
*/
|
|
@@ -91,6 +107,7 @@ export declare class DatalyrSDK {
|
|
|
91
107
|
updateAutoEventsConfig(config: Partial<AutoEventConfig>): void;
|
|
92
108
|
/**
|
|
93
109
|
* Track event with automatic SKAdNetwork conversion value encoding
|
|
110
|
+
* Uses SKAN 4.0 on iOS 16.1+ with coarse values and lock window support
|
|
94
111
|
*/
|
|
95
112
|
trackWithSKAdNetwork(event: string, properties?: EventData): Promise<void>;
|
|
96
113
|
/**
|
|
@@ -140,12 +157,18 @@ export declare class DatalyrSDK {
|
|
|
140
157
|
meta: boolean;
|
|
141
158
|
tiktok: boolean;
|
|
142
159
|
appleSearchAds: boolean;
|
|
160
|
+
playInstallReferrer: boolean;
|
|
143
161
|
};
|
|
144
162
|
/**
|
|
145
163
|
* Get Apple Search Ads attribution data
|
|
146
164
|
* Returns attribution if user installed via Apple Search Ads, null otherwise
|
|
147
165
|
*/
|
|
148
166
|
getAppleSearchAdsAttribution(): AppleSearchAdsAttribution | null;
|
|
167
|
+
/**
|
|
168
|
+
* Get Google Play Install Referrer attribution data (Android only)
|
|
169
|
+
* Returns referrer data if available, null otherwise
|
|
170
|
+
*/
|
|
171
|
+
getPlayInstallReferrer(): Record<string, any> | null;
|
|
149
172
|
/**
|
|
150
173
|
* Update tracking authorization status on all platform SDKs
|
|
151
174
|
* Call this AFTER the user responds to the ATT permission dialog
|
|
@@ -171,6 +194,11 @@ export declare class DatalyrSDK {
|
|
|
171
194
|
* Persist user data to storage
|
|
172
195
|
*/
|
|
173
196
|
private persistUserData;
|
|
197
|
+
/**
|
|
198
|
+
* Initialize network status monitoring
|
|
199
|
+
* Automatically updates event queue when network status changes
|
|
200
|
+
*/
|
|
201
|
+
private initializeNetworkMonitoring;
|
|
174
202
|
/**
|
|
175
203
|
* Set up app state monitoring for lifecycle events (optimized)
|
|
176
204
|
*/
|
|
@@ -221,6 +249,7 @@ export declare class Datalyr {
|
|
|
221
249
|
currentUserId?: string;
|
|
222
250
|
queueStats: any;
|
|
223
251
|
attribution: any;
|
|
252
|
+
journey: any;
|
|
224
253
|
};
|
|
225
254
|
static getAnonymousId(): string;
|
|
226
255
|
static getAttributionData(): AttributionData;
|
package/lib/datalyr-sdk.js
CHANGED
|
@@ -3,14 +3,17 @@ import { getOrCreateVisitorId, getOrCreateAnonymousId, getOrCreateSessionId, cre
|
|
|
3
3
|
import { createHttpClient, HttpClient } from './http-client';
|
|
4
4
|
import { createEventQueue, EventQueue } from './event-queue';
|
|
5
5
|
import { attributionManager } from './attribution';
|
|
6
|
+
import { journeyManager } from './journey';
|
|
6
7
|
import { AutoEventsManager } from './auto-events';
|
|
7
8
|
import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
8
9
|
import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
9
|
-
import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
10
|
+
import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
11
|
+
import { networkStatusManager } from './network-status';
|
|
10
12
|
export class DatalyrSDK {
|
|
11
13
|
constructor() {
|
|
12
14
|
this.autoEventsManager = null;
|
|
13
15
|
this.appStateSubscription = null;
|
|
16
|
+
this.networkStatusUnsubscribe = null;
|
|
14
17
|
// Initialize state with defaults
|
|
15
18
|
this.state = {
|
|
16
19
|
initialized: false,
|
|
@@ -72,15 +75,33 @@ export class DatalyrSDK {
|
|
|
72
75
|
flushInterval: this.state.config.flushInterval || 30000,
|
|
73
76
|
maxRetryCount: this.state.config.maxRetries || 3,
|
|
74
77
|
});
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
// PARALLEL INITIALIZATION: IDs and core managers
|
|
79
|
+
// Run ID creation and core manager initialization in parallel for faster startup
|
|
80
|
+
const [visitorId, anonymousId, sessionId] = await Promise.all([
|
|
81
|
+
getOrCreateVisitorId(),
|
|
82
|
+
getOrCreateAnonymousId(),
|
|
83
|
+
getOrCreateSessionId(),
|
|
84
|
+
// These run concurrently but don't return values we need to capture
|
|
85
|
+
this.loadPersistedUserData(),
|
|
86
|
+
this.state.config.enableAttribution ? attributionManager.initialize() : Promise.resolve(),
|
|
87
|
+
journeyManager.initialize(),
|
|
88
|
+
]);
|
|
89
|
+
this.state.visitorId = visitorId;
|
|
90
|
+
this.state.anonymousId = anonymousId;
|
|
91
|
+
this.state.sessionId = sessionId;
|
|
92
|
+
// Record initial attribution to journey if this is a new session with attribution
|
|
93
|
+
const initialAttribution = attributionManager.getAttributionData();
|
|
94
|
+
if (initialAttribution.utm_source || initialAttribution.fbclid || initialAttribution.gclid || initialAttribution.lyr) {
|
|
95
|
+
await journeyManager.recordAttribution(this.state.sessionId, {
|
|
96
|
+
source: initialAttribution.utm_source || initialAttribution.campaign_source,
|
|
97
|
+
medium: initialAttribution.utm_medium || initialAttribution.campaign_medium,
|
|
98
|
+
campaign: initialAttribution.utm_campaign || initialAttribution.campaign_name,
|
|
99
|
+
fbclid: initialAttribution.fbclid,
|
|
100
|
+
gclid: initialAttribution.gclid,
|
|
101
|
+
ttclid: initialAttribution.ttclid,
|
|
102
|
+
clickIdType: initialAttribution.fbclid ? 'fbclid' : initialAttribution.gclid ? 'gclid' : initialAttribution.ttclid ? 'ttclid' : undefined,
|
|
103
|
+
lyr: initialAttribution.lyr,
|
|
104
|
+
});
|
|
84
105
|
}
|
|
85
106
|
// Initialize auto-events manager (asynchronously to avoid blocking)
|
|
86
107
|
if (this.state.config.enableAutoEvents) {
|
|
@@ -105,7 +126,7 @@ export class DatalyrSDK {
|
|
|
105
126
|
errorLog('Error setting up app state monitoring (non-blocking):', error);
|
|
106
127
|
}
|
|
107
128
|
}, 50);
|
|
108
|
-
// Initialize SKAdNetwork conversion encoder
|
|
129
|
+
// Initialize SKAdNetwork conversion encoder (synchronous, no await needed)
|
|
109
130
|
if (config.skadTemplate) {
|
|
110
131
|
const template = ConversionTemplates[config.skadTemplate];
|
|
111
132
|
if (template) {
|
|
@@ -117,27 +138,39 @@ export class DatalyrSDK {
|
|
|
117
138
|
}
|
|
118
139
|
}
|
|
119
140
|
}
|
|
120
|
-
//
|
|
141
|
+
// PARALLEL INITIALIZATION: Network monitoring and platform integrations
|
|
142
|
+
// These are independent and can run concurrently for faster startup
|
|
143
|
+
const platformInitPromises = [
|
|
144
|
+
// Network monitoring
|
|
145
|
+
this.initializeNetworkMonitoring(),
|
|
146
|
+
// Apple Search Ads (iOS only)
|
|
147
|
+
appleSearchAdsIntegration.initialize(config.debug),
|
|
148
|
+
// Google Play Install Referrer (Android only)
|
|
149
|
+
playInstallReferrerIntegration.initialize(),
|
|
150
|
+
];
|
|
151
|
+
// Add Meta initialization if configured
|
|
121
152
|
if ((_b = config.meta) === null || _b === void 0 ? void 0 : _b.appId) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
153
|
+
platformInitPromises.push(metaIntegration.initialize(config.meta, config.debug).then(async () => {
|
|
154
|
+
// After Meta initializes, fetch deferred deep link
|
|
155
|
+
if (config.enableAttribution !== false) {
|
|
156
|
+
const deferredLink = await metaIntegration.fetchDeferredDeepLink();
|
|
157
|
+
if (deferredLink) {
|
|
158
|
+
await this.handleDeferredDeepLink(deferredLink);
|
|
159
|
+
}
|
|
128
160
|
}
|
|
129
|
-
}
|
|
161
|
+
}));
|
|
130
162
|
}
|
|
131
|
-
//
|
|
163
|
+
// Add TikTok initialization if configured
|
|
132
164
|
if (((_c = config.tiktok) === null || _c === void 0 ? void 0 : _c.appId) && ((_d = config.tiktok) === null || _d === void 0 ? void 0 : _d.tiktokAppId)) {
|
|
133
|
-
|
|
165
|
+
platformInitPromises.push(tiktokIntegration.initialize(config.tiktok, config.debug));
|
|
134
166
|
}
|
|
135
|
-
//
|
|
136
|
-
await
|
|
167
|
+
// Wait for all platform integrations to complete
|
|
168
|
+
await Promise.all(platformInitPromises);
|
|
137
169
|
debugLog('Platform integrations initialized', {
|
|
138
170
|
meta: metaIntegration.isAvailable(),
|
|
139
171
|
tiktok: tiktokIntegration.isAvailable(),
|
|
140
172
|
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
173
|
+
playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
|
|
141
174
|
});
|
|
142
175
|
// SDK initialized successfully - set state before tracking install event
|
|
143
176
|
this.state.initialized = true;
|
|
@@ -398,6 +431,7 @@ export class DatalyrSDK {
|
|
|
398
431
|
currentUserId: this.state.currentUserId,
|
|
399
432
|
queueStats: this.eventQueue.getStats(),
|
|
400
433
|
attribution: attributionManager.getAttributionSummary(),
|
|
434
|
+
journey: journeyManager.getJourneySummary(),
|
|
401
435
|
};
|
|
402
436
|
}
|
|
403
437
|
/**
|
|
@@ -407,10 +441,28 @@ export class DatalyrSDK {
|
|
|
407
441
|
return this.state.anonymousId;
|
|
408
442
|
}
|
|
409
443
|
/**
|
|
410
|
-
* Get detailed attribution data
|
|
444
|
+
* Get detailed attribution data (includes journey tracking data)
|
|
411
445
|
*/
|
|
412
446
|
getAttributionData() {
|
|
413
|
-
|
|
447
|
+
const attribution = attributionManager.getAttributionData();
|
|
448
|
+
const journeyData = journeyManager.getAttributionData();
|
|
449
|
+
// Merge attribution with journey data
|
|
450
|
+
return {
|
|
451
|
+
...attribution,
|
|
452
|
+
...journeyData,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Get journey tracking summary
|
|
457
|
+
*/
|
|
458
|
+
getJourneySummary() {
|
|
459
|
+
return journeyManager.getJourneySummary();
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Get full customer journey (all touchpoints)
|
|
463
|
+
*/
|
|
464
|
+
getJourney() {
|
|
465
|
+
return journeyManager.getJourney();
|
|
414
466
|
}
|
|
415
467
|
/**
|
|
416
468
|
* Set custom attribution data (for testing or manual attribution)
|
|
@@ -460,22 +512,25 @@ export class DatalyrSDK {
|
|
|
460
512
|
// MARK: - SKAdNetwork Enhanced Methods
|
|
461
513
|
/**
|
|
462
514
|
* Track event with automatic SKAdNetwork conversion value encoding
|
|
515
|
+
* Uses SKAN 4.0 on iOS 16.1+ with coarse values and lock window support
|
|
463
516
|
*/
|
|
464
517
|
async trackWithSKAdNetwork(event, properties) {
|
|
465
518
|
// Existing tracking (keep exactly as-is)
|
|
466
519
|
await this.track(event, properties);
|
|
467
|
-
//
|
|
520
|
+
// Automatic SKAdNetwork encoding with SKAN 4.0 support
|
|
468
521
|
if (!DatalyrSDK.conversionEncoder) {
|
|
469
522
|
if (DatalyrSDK.debugEnabled) {
|
|
470
523
|
errorLog('SKAdNetwork encoder not initialized. Pass skadTemplate in initialize()');
|
|
471
524
|
}
|
|
472
525
|
return;
|
|
473
526
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
527
|
+
// Use SKAN 4.0 encoding (includes coarse value and lock window)
|
|
528
|
+
const result = DatalyrSDK.conversionEncoder.encodeWithSKAN4(event, properties);
|
|
529
|
+
if (result.fineValue > 0 || result.priority > 0) {
|
|
530
|
+
// Use SKAN 4.0 method (automatically falls back to SKAN 3.0 on older iOS)
|
|
531
|
+
const success = await SKAdNetworkBridge.updatePostbackConversionValue(result);
|
|
477
532
|
if (DatalyrSDK.debugEnabled) {
|
|
478
|
-
debugLog(`
|
|
533
|
+
debugLog(`SKAN: event=${event}, fine=${result.fineValue}, coarse=${result.coarseValue}, lock=${result.lockWindow}, success=${success}`, properties);
|
|
479
534
|
}
|
|
480
535
|
}
|
|
481
536
|
else if (DatalyrSDK.debugEnabled) {
|
|
@@ -676,6 +731,7 @@ export class DatalyrSDK {
|
|
|
676
731
|
meta: metaIntegration.isAvailable(),
|
|
677
732
|
tiktok: tiktokIntegration.isAvailable(),
|
|
678
733
|
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
734
|
+
playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
|
|
679
735
|
};
|
|
680
736
|
}
|
|
681
737
|
/**
|
|
@@ -685,6 +741,14 @@ export class DatalyrSDK {
|
|
|
685
741
|
getAppleSearchAdsAttribution() {
|
|
686
742
|
return appleSearchAdsIntegration.getAttributionData();
|
|
687
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* Get Google Play Install Referrer attribution data (Android only)
|
|
746
|
+
* Returns referrer data if available, null otherwise
|
|
747
|
+
*/
|
|
748
|
+
getPlayInstallReferrer() {
|
|
749
|
+
const data = playInstallReferrerIntegration.getReferrerData();
|
|
750
|
+
return data ? playInstallReferrerIntegration.getAttributionData() : null;
|
|
751
|
+
}
|
|
688
752
|
/**
|
|
689
753
|
* Update tracking authorization status on all platform SDKs
|
|
690
754
|
* Call this AFTER the user responds to the ATT permission dialog
|
|
@@ -849,6 +913,41 @@ export class DatalyrSDK {
|
|
|
849
913
|
errorLog('Error persisting user data:', error);
|
|
850
914
|
}
|
|
851
915
|
}
|
|
916
|
+
/**
|
|
917
|
+
* Initialize network status monitoring
|
|
918
|
+
* Automatically updates event queue when network status changes
|
|
919
|
+
*/
|
|
920
|
+
async initializeNetworkMonitoring() {
|
|
921
|
+
try {
|
|
922
|
+
await networkStatusManager.initialize();
|
|
923
|
+
// Update event queue with current network status
|
|
924
|
+
this.state.isOnline = networkStatusManager.isOnline();
|
|
925
|
+
this.eventQueue.setOnlineStatus(this.state.isOnline);
|
|
926
|
+
// Subscribe to network changes
|
|
927
|
+
this.networkStatusUnsubscribe = networkStatusManager.subscribe((state) => {
|
|
928
|
+
const isOnline = state.isConnected && (state.isInternetReachable !== false);
|
|
929
|
+
this.state.isOnline = isOnline;
|
|
930
|
+
this.eventQueue.setOnlineStatus(isOnline);
|
|
931
|
+
// Track network status change event (only if SDK is fully initialized)
|
|
932
|
+
if (this.state.initialized) {
|
|
933
|
+
this.track('$network_status_change', {
|
|
934
|
+
is_online: isOnline,
|
|
935
|
+
network_type: state.type,
|
|
936
|
+
is_internet_reachable: state.isInternetReachable,
|
|
937
|
+
}).catch(() => {
|
|
938
|
+
// Ignore errors for network status events
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
debugLog(`Network monitoring initialized, online: ${this.state.isOnline}`);
|
|
943
|
+
}
|
|
944
|
+
catch (error) {
|
|
945
|
+
errorLog('Error initializing network monitoring (non-blocking):', error);
|
|
946
|
+
// Default to online if monitoring fails
|
|
947
|
+
this.state.isOnline = true;
|
|
948
|
+
this.eventQueue.setOnlineStatus(true);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
852
951
|
/**
|
|
853
952
|
* Set up app state monitoring for lifecycle events (optimized)
|
|
854
953
|
*/
|
|
@@ -869,6 +968,8 @@ export class DatalyrSDK {
|
|
|
869
968
|
else if (nextAppState === 'active') {
|
|
870
969
|
// App became active, ensure we have fresh session if needed
|
|
871
970
|
this.refreshSession();
|
|
971
|
+
// Refresh network status when coming back from background
|
|
972
|
+
networkStatusManager.refresh();
|
|
872
973
|
// Notify auto-events manager for session handling
|
|
873
974
|
if (this.autoEventsManager) {
|
|
874
975
|
this.autoEventsManager.handleAppForeground();
|
|
@@ -906,6 +1007,13 @@ export class DatalyrSDK {
|
|
|
906
1007
|
this.appStateSubscription.remove();
|
|
907
1008
|
this.appStateSubscription = null;
|
|
908
1009
|
}
|
|
1010
|
+
// Remove network status listener
|
|
1011
|
+
if (this.networkStatusUnsubscribe) {
|
|
1012
|
+
this.networkStatusUnsubscribe();
|
|
1013
|
+
this.networkStatusUnsubscribe = null;
|
|
1014
|
+
}
|
|
1015
|
+
// Destroy network status manager
|
|
1016
|
+
networkStatusManager.destroy();
|
|
909
1017
|
// Destroy event queue
|
|
910
1018
|
this.eventQueue.destroy();
|
|
911
1019
|
// Reset state
|
package/lib/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const datalyr: DatalyrSDK;
|
|
|
3
3
|
export { Datalyr };
|
|
4
4
|
export * from './types';
|
|
5
5
|
export { attributionManager } from './attribution';
|
|
6
|
+
export { journeyManager } from './journey';
|
|
7
|
+
export type { TouchAttribution, TouchPoint } from './journey';
|
|
6
8
|
export { createAutoEventsManager, AutoEventsManager } from './auto-events';
|
|
7
9
|
export * from './utils';
|
|
8
10
|
export * from './http-client';
|
|
@@ -10,6 +12,8 @@ export * from './event-queue';
|
|
|
10
12
|
export { DatalyrSDK };
|
|
11
13
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
12
14
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
13
|
-
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
15
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
16
|
+
export { networkStatusManager } from './network-status';
|
|
17
|
+
export type { NetworkState, NetworkStateListener } from './network-status';
|
|
14
18
|
export type { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
|
|
15
19
|
export default DatalyrSDK;
|
package/lib/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { Datalyr };
|
|
|
8
8
|
// Export types and utilities
|
|
9
9
|
export * from './types';
|
|
10
10
|
export { attributionManager } from './attribution';
|
|
11
|
+
export { journeyManager } from './journey';
|
|
11
12
|
export { createAutoEventsManager, AutoEventsManager } from './auto-events';
|
|
12
13
|
// Re-export utilities for advanced usage
|
|
13
14
|
export * from './utils';
|
|
@@ -19,6 +20,8 @@ export { DatalyrSDK };
|
|
|
19
20
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
20
21
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
21
22
|
// Export platform integrations
|
|
22
|
-
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
23
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
24
|
+
// Export network status manager
|
|
25
|
+
export { networkStatusManager } from './network-status';
|
|
23
26
|
// Default export for compatibility
|
|
24
27
|
export default DatalyrSDK;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform SDK Integrations
|
|
3
|
-
* Meta (Facebook), TikTok,
|
|
3
|
+
* Meta (Facebook), TikTok, Apple Search Ads, and Google Play Install Referrer SDK wrappers
|
|
4
4
|
*/
|
|
5
5
|
export { MetaIntegration, metaIntegration } from './meta-integration';
|
|
6
6
|
export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
|
|
7
7
|
export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
|
|
8
|
+
export { playInstallReferrerIntegration } from './play-install-referrer';
|
|
9
|
+
export type { PlayInstallReferrer } from './play-install-referrer';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform SDK Integrations
|
|
3
|
-
* Meta (Facebook), TikTok,
|
|
3
|
+
* Meta (Facebook), TikTok, Apple Search Ads, and Google Play Install Referrer SDK wrappers
|
|
4
4
|
*/
|
|
5
5
|
export { MetaIntegration, metaIntegration } from './meta-integration';
|
|
6
6
|
export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
|
|
7
7
|
export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
|
|
8
|
+
export { playInstallReferrerIntegration } from './play-install-referrer';
|