@magicpixel/rn-mp-client-sdk 1.13.0 → 1.13.20
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 +163 -14
- package/lib/commonjs/common/app-types.js.map +1 -1
- package/lib/commonjs/common/constants.js +11 -2
- package/lib/commonjs/common/constants.js.map +1 -1
- package/lib/commonjs/common/data-store.js +13 -30
- package/lib/commonjs/common/data-store.js.map +1 -1
- package/lib/commonjs/common/deeplink-helper.js +174 -0
- package/lib/commonjs/common/deeplink-helper.js.map +1 -0
- package/lib/commonjs/common/device-info-helper.js +168 -0
- package/lib/commonjs/common/device-info-helper.js.map +1 -0
- package/lib/commonjs/common/event-bus.js +39 -0
- package/lib/commonjs/common/event-bus.js.map +1 -1
- package/lib/commonjs/common/network-service.js +119 -15
- package/lib/commonjs/common/network-service.js.map +1 -1
- package/lib/commonjs/common/reporter.js +28 -10
- package/lib/commonjs/common/reporter.js.map +1 -1
- package/lib/commonjs/common/storage-helper.js +227 -0
- package/lib/commonjs/common/storage-helper.js.map +1 -0
- package/lib/commonjs/common/utils.js +20 -2
- package/lib/commonjs/common/utils.js.map +1 -1
- package/lib/commonjs/eedl/eedl.js +198 -44
- package/lib/commonjs/eedl/eedl.js.map +1 -1
- package/lib/commonjs/index.js +290 -48
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/mp-client-sdk.js +17 -10
- package/lib/commonjs/models/mp-client-sdk.js.map +1 -1
- package/lib/commonjs/processors/data-element.processor.js +51 -7
- package/lib/commonjs/processors/data-element.processor.js.map +1 -1
- package/lib/commonjs/processors/visit-id.processor.js +78 -15
- package/lib/commonjs/processors/visit-id.processor.js.map +1 -1
- package/lib/module/common/app-types.js.map +1 -1
- package/lib/module/common/constants.js +11 -2
- package/lib/module/common/constants.js.map +1 -1
- package/lib/module/common/data-store.js +13 -30
- package/lib/module/common/data-store.js.map +1 -1
- package/lib/module/common/deeplink-helper.js +168 -0
- package/lib/module/common/deeplink-helper.js.map +1 -0
- package/lib/module/common/device-info-helper.js +161 -0
- package/lib/module/common/device-info-helper.js.map +1 -0
- package/lib/module/common/event-bus.js +39 -0
- package/lib/module/common/event-bus.js.map +1 -1
- package/lib/module/common/network-service.js +119 -15
- package/lib/module/common/network-service.js.map +1 -1
- package/lib/module/common/reporter.js +29 -10
- package/lib/module/common/reporter.js.map +1 -1
- package/lib/module/common/storage-helper.js +221 -0
- package/lib/module/common/storage-helper.js.map +1 -0
- package/lib/module/common/utils.js +20 -2
- package/lib/module/common/utils.js.map +1 -1
- package/lib/module/eedl/eedl.js +198 -44
- package/lib/module/eedl/eedl.js.map +1 -1
- package/lib/module/index.js +279 -47
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/mp-client-sdk.js +16 -9
- package/lib/module/models/mp-client-sdk.js.map +1 -1
- package/lib/module/processors/data-element.processor.js +51 -7
- package/lib/module/processors/data-element.processor.js.map +1 -1
- package/lib/module/processors/visit-id.processor.js +78 -15
- package/lib/module/processors/visit-id.processor.js.map +1 -1
- package/lib/typescript/{common → src/common}/app-types.d.ts +29 -9
- package/lib/typescript/{common → src/common}/constants.d.ts +0 -1
- package/lib/typescript/{common → src/common}/data-store.d.ts +3 -8
- package/lib/typescript/src/common/deeplink-helper.d.ts +60 -0
- package/lib/typescript/src/common/device-info-helper.d.ts +54 -0
- package/lib/typescript/src/common/event-bus.d.ts +21 -0
- package/lib/typescript/src/common/network-service.d.ts +32 -0
- package/lib/typescript/src/common/storage-helper.d.ts +47 -0
- package/lib/typescript/{common → src/common}/utils.d.ts +7 -0
- package/lib/typescript/{eedl → src/eedl}/eedl.d.ts +43 -1
- package/lib/typescript/{index.d.ts → src/index.d.ts} +39 -5
- package/lib/typescript/{models → src/models}/mp-client-sdk.d.ts +7 -0
- package/lib/typescript/src/processors/visit-id.processor.d.ts +23 -0
- package/package.json +26 -37
- package/src/common/app-types.ts +32 -10
- package/src/common/constants.ts +0 -6
- package/src/common/data-store.ts +8 -30
- package/src/common/deeplink-helper.ts +181 -0
- package/src/common/device-info-helper.ts +190 -0
- package/src/common/event-bus.ts +39 -0
- package/src/common/network-service.ts +154 -21
- package/src/common/reporter.ts +31 -10
- package/src/common/storage-helper.ts +266 -0
- package/src/common/utils.ts +20 -2
- package/src/eedl/eedl.ts +225 -51
- package/src/index.tsx +332 -67
- package/src/models/mp-client-sdk.ts +8 -0
- package/src/processors/data-element.processor.ts +85 -7
- package/src/processors/visit-id.processor.ts +92 -22
- package/lib/commonjs/processors/trans-function.processor.js +0 -73
- package/lib/commonjs/processors/trans-function.processor.js.map +0 -1
- package/lib/module/processors/trans-function.processor.js +0 -66
- package/lib/module/processors/trans-function.processor.js.map +0 -1
- package/lib/typescript/common/event-bus.d.ts +0 -6
- package/lib/typescript/common/network-service.d.ts +0 -8
- package/lib/typescript/processors/trans-function.processor.d.ts +0 -12
- package/lib/typescript/processors/visit-id.processor.d.ts +0 -9
- package/src/processors/trans-function.processor.ts +0 -85
- /package/lib/typescript/{common → src/common}/logger.d.ts +0 -0
- /package/lib/typescript/{common → src/common}/reporter.d.ts +0 -0
- /package/lib/typescript/{models → src/models}/geo-api-response.d.ts +0 -0
- /package/lib/typescript/{processors → src/processors}/data-element.processor.d.ts +0 -0
- /package/lib/typescript/{processors → src/processors}/geo-location.processor.d.ts +0 -0
- /package/lib/typescript/{processors → src/processors}/qc.processor.d.ts +0 -0
- /package/lib/typescript/{processors → src/processors}/tag.processor.d.ts +0 -0
package/src/index.tsx
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Polyfill for crypto.getRandomValues() required by ULID
|
|
2
|
+
import 'react-native-get-random-values';
|
|
3
|
+
|
|
1
4
|
import { MpDataLayerHelper } from './eedl/eedl';
|
|
2
5
|
import type {
|
|
3
6
|
AppCustomerInfo,
|
|
@@ -17,9 +20,22 @@ import { NetworkService } from './common/network-service';
|
|
|
17
20
|
import { EventBus } from './common/event-bus';
|
|
18
21
|
import { VisitIdProcessor } from './processors/visit-id.processor';
|
|
19
22
|
import { GeoLocationProcessor } from './processors/geo-location.processor';
|
|
23
|
+
import { DeviceInfoHelper } from './common/device-info-helper';
|
|
24
|
+
import { DeepLinkHelper } from './common/deeplink-helper';
|
|
25
|
+
import { StorageHelper } from './common/storage-helper';
|
|
20
26
|
|
|
21
27
|
const DL_INIT_EVENT = 'page_load';
|
|
22
28
|
|
|
29
|
+
// Maximum number of events to buffer before SDK is ready
|
|
30
|
+
const MAX_BUFFERED_EVENTS = 500;
|
|
31
|
+
|
|
32
|
+
// Type for buffered events
|
|
33
|
+
interface BufferedEvent {
|
|
34
|
+
type: 'pageLoad' | 'event';
|
|
35
|
+
name?: string; // event name for recordEvent
|
|
36
|
+
data: any;
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
class MagicPixelImpl {
|
|
24
40
|
private static dl: MpDataLayerHelper = new MpDataLayerHelper(
|
|
25
41
|
'mpDlEvent',
|
|
@@ -31,26 +47,131 @@ class MagicPixelImpl {
|
|
|
31
47
|
private static customerIdentifiers: MapLike = {};
|
|
32
48
|
|
|
33
49
|
private static deepLinkUrl: string | undefined = undefined;
|
|
50
|
+
private static orgId: string | undefined = undefined;
|
|
34
51
|
|
|
35
52
|
private static firstAppLaunch = true;
|
|
36
53
|
|
|
37
|
-
|
|
54
|
+
// SDK readiness state and event buffer
|
|
55
|
+
private static isReady = false;
|
|
56
|
+
private static isInitializing = false;
|
|
57
|
+
private static eventBuffer: BufferedEvent[] = [];
|
|
58
|
+
private static onInitFailureCallback: ((error: Error) => void) | undefined;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initialize the MagicPixel SDK
|
|
62
|
+
* This method is synchronous - it stores config immediately and runs async setup in background
|
|
63
|
+
* Events called before async setup completes are buffered and processed once ready
|
|
64
|
+
* @param options SDK initialization options
|
|
65
|
+
*/
|
|
66
|
+
static init(options: SdkInitOptions): void {
|
|
67
|
+
// Guard against multiple init() calls
|
|
68
|
+
if (this.isReady) {
|
|
69
|
+
Logger.logDbg(
|
|
70
|
+
'MagicPixel SDK already initialized. Ignoring duplicate init() call.'
|
|
71
|
+
);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this.isInitializing) {
|
|
76
|
+
Logger.logDbg(
|
|
77
|
+
'MagicPixel SDK initialization in progress. Ignoring duplicate init() call.'
|
|
78
|
+
);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.isInitializing = true;
|
|
83
|
+
|
|
84
|
+
// Sync setup - happens immediately
|
|
38
85
|
Logger.setLogLevel(options.logLevel ?? 'none');
|
|
86
|
+
this.orgId = options.orgId;
|
|
87
|
+
this.onInitFailureCallback = options.onInitFailure;
|
|
88
|
+
|
|
89
|
+
Logger.logDbg('MagicPixel SDK init started');
|
|
90
|
+
|
|
91
|
+
// Async setup - runs in background, events are buffered until complete
|
|
92
|
+
this.initAsync(options).catch((err) => {
|
|
93
|
+
Logger.logError('MagicPixel SDK initialization failed:', err);
|
|
94
|
+
Reporter.reportError('sdk_init', err);
|
|
95
|
+
|
|
96
|
+
// Reset initializing flag so developer can retry if needed
|
|
97
|
+
this.isInitializing = false;
|
|
98
|
+
|
|
99
|
+
// Call failure callback if provided
|
|
100
|
+
if (this.onInitFailureCallback) {
|
|
101
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
102
|
+
this.onInitFailureCallback(error);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Shutdown the SDK and release all resources
|
|
109
|
+
* Call this when the app is closing or when you need to reinitialize the SDK
|
|
110
|
+
* After shutdown, you must call init() again to use the SDK
|
|
111
|
+
*/
|
|
112
|
+
static shutdown(): void {
|
|
113
|
+
Logger.logDbg('MagicPixel SDK shutting down...');
|
|
114
|
+
|
|
115
|
+
// Clean up deeplink listener and callback
|
|
116
|
+
DeepLinkHelper.cleanup();
|
|
117
|
+
|
|
118
|
+
// Clear all EventBus listeners to prevent duplicates on reinit
|
|
119
|
+
EventBus.clearAll();
|
|
120
|
+
|
|
121
|
+
// Clear session-scoped data element values (in-memory store)
|
|
122
|
+
StorageHelper.clearSessionStore();
|
|
123
|
+
|
|
124
|
+
// Clear event buffer
|
|
125
|
+
this.eventBuffer = [];
|
|
126
|
+
|
|
127
|
+
// Reset state flags
|
|
128
|
+
this.isReady = false;
|
|
129
|
+
this.isInitializing = false;
|
|
130
|
+
this.onInitFailureCallback = undefined;
|
|
131
|
+
|
|
132
|
+
// Reset customer data
|
|
133
|
+
this.customerInfo = undefined as any;
|
|
134
|
+
this.customerIdentifiers = {};
|
|
135
|
+
this.deepLinkUrl = undefined;
|
|
136
|
+
this.firstAppLaunch = true;
|
|
137
|
+
|
|
138
|
+
// Shutdown EEDL
|
|
139
|
+
this.dl.shutdown();
|
|
140
|
+
|
|
141
|
+
Logger.logDbg('MagicPixel SDK shutdown complete');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Async initialization - runs in background
|
|
146
|
+
*/
|
|
147
|
+
private static async initAsync(options: SdkInitOptions): Promise<void> {
|
|
39
148
|
await NetworkService.refreshClientSdkJson(options);
|
|
40
149
|
|
|
41
150
|
if (!DataStore.isDataStoreReady()) {
|
|
42
|
-
throw new Error(
|
|
151
|
+
throw new Error(
|
|
152
|
+
'MagicPixel SDK: DataStore not ready after config fetch. Initialization failed.'
|
|
153
|
+
);
|
|
43
154
|
}
|
|
44
155
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
156
|
+
// Initialize StorageHelper for data element storage duration
|
|
157
|
+
// This loads visitor-scoped values from AsyncStorage and clears expired ones (>30 days)
|
|
158
|
+
await StorageHelper.initialize();
|
|
159
|
+
|
|
160
|
+
// Auto-detect device info - always use detected values to prevent hardcoded values
|
|
161
|
+
const autoDetectedInfo = await DeviceInfoHelper.getAppInfo();
|
|
162
|
+
Logger.logDbg('Auto-detected device info:', autoDetectedInfo);
|
|
163
|
+
|
|
164
|
+
// Set device type from auto-detection (Expo/react-native-device-info)
|
|
165
|
+
// This is more reliable than dimension-based detection which fails on high-res phones
|
|
166
|
+
const detectedDeviceType = autoDetectedInfo.is_tablet ? 'tablet' : 'mobile';
|
|
167
|
+
DataStore.overrideDeviceType(detectedDeviceType);
|
|
168
|
+
Logger.logDbg('Device type set from auto-detection:', detectedDeviceType);
|
|
52
169
|
|
|
53
|
-
|
|
170
|
+
// Always use auto-detected app_version (ignore options.app_version)
|
|
171
|
+
this.setAppVersion(autoDetectedInfo.app_version);
|
|
172
|
+
|
|
173
|
+
// Set device info with auto-detected values (cannot be overridden)
|
|
174
|
+
await this.setDeviceInfo();
|
|
54
175
|
|
|
55
176
|
await VisitIdProcessor.init(options?.orgId);
|
|
56
177
|
|
|
@@ -62,9 +183,39 @@ class MagicPixelImpl {
|
|
|
62
183
|
Logger.logDbg('No facebook client id found. not setting');
|
|
63
184
|
}
|
|
64
185
|
|
|
186
|
+
// Auto-detect deeplink if app was opened via deeplink (e.g., ad click)
|
|
187
|
+
// Supports: custom schemes (myapp://), universal links (https://), app links, HTTP links
|
|
188
|
+
const initialDeepLink = await DeepLinkHelper.initialize((url, linkType) => {
|
|
189
|
+
try {
|
|
190
|
+
Logger.logDbg(`Deeplink detected (${linkType}):`, url);
|
|
191
|
+
|
|
192
|
+
// Store deeplink URL - will be attached to next page_load event
|
|
193
|
+
this.setDeepLinkUrl(url);
|
|
194
|
+
|
|
195
|
+
// Fire deeplink opened event
|
|
196
|
+
// URL params will be parsed and attached to page_load event automatically
|
|
197
|
+
this.recordEvent('deeplink_opened', {
|
|
198
|
+
deep_link_url: url,
|
|
199
|
+
link_type: linkType,
|
|
200
|
+
});
|
|
201
|
+
} catch (err) {
|
|
202
|
+
Logger.logError('Error processing deeplink:', err);
|
|
203
|
+
Reporter.reportError('deeplink_callback', err);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (initialDeepLink) {
|
|
208
|
+
Logger.logDbg('App opened with initial deeplink:', initialDeepLink);
|
|
209
|
+
}
|
|
210
|
+
|
|
65
211
|
// Initialize EEDL with any global event listeners
|
|
66
212
|
await this.dl.init({});
|
|
67
213
|
|
|
214
|
+
// Set event deduplication window if specified (default is 5000ms)
|
|
215
|
+
if (typeof options.eventDeduplicationWindowMs !== 'undefined') {
|
|
216
|
+
this.dl.setDeduplicationWindow(options.eventDeduplicationWindowMs);
|
|
217
|
+
}
|
|
218
|
+
|
|
68
219
|
// Make geo location API call after initialization
|
|
69
220
|
try {
|
|
70
221
|
await GeoLocationProcessor.makeGeoLocationApiCall();
|
|
@@ -81,15 +232,14 @@ class MagicPixelImpl {
|
|
|
81
232
|
Logger.logDbg('Tracking Event:: ', eventName, 'with id:: ', eventId);
|
|
82
233
|
|
|
83
234
|
if (DataStore.shouldExecuteTMForEvent(eventName)) {
|
|
84
|
-
//
|
|
85
|
-
|
|
235
|
+
// Call runTM directly - no queuing, EEDL manages sequential processing
|
|
236
|
+
await this.runTM(
|
|
86
237
|
false,
|
|
87
238
|
`custom_event_${eventName}`,
|
|
88
239
|
eventName,
|
|
89
240
|
eventId,
|
|
90
241
|
eventDataModel
|
|
91
242
|
);
|
|
92
|
-
await this.checkAndFireTM();
|
|
93
243
|
}
|
|
94
244
|
});
|
|
95
245
|
|
|
@@ -100,9 +250,38 @@ class MagicPixelImpl {
|
|
|
100
250
|
}
|
|
101
251
|
|
|
102
252
|
this.ready();
|
|
253
|
+
|
|
254
|
+
// Mark SDK as ready and flush any buffered events
|
|
255
|
+
this.isReady = true;
|
|
256
|
+
this.isInitializing = false;
|
|
257
|
+
Logger.logDbg('SDK is ready, flushing event buffer');
|
|
258
|
+
this.flushEventBuffer();
|
|
103
259
|
}
|
|
104
260
|
|
|
105
261
|
static recordEvent(eventName: string, payload: MapLike): void {
|
|
262
|
+
// Buffer event if SDK is not ready yet
|
|
263
|
+
if (!this.isReady) {
|
|
264
|
+
Logger.logDbg(`SDK not ready, buffering event: ${eventName}`);
|
|
265
|
+
// Evict oldest event if buffer at capacity
|
|
266
|
+
if (this.eventBuffer.length >= MAX_BUFFERED_EVENTS) {
|
|
267
|
+
const evicted = this.eventBuffer.shift();
|
|
268
|
+
Logger.logDbg(
|
|
269
|
+
`Event buffer at capacity (${MAX_BUFFERED_EVENTS}), evicted oldest:`,
|
|
270
|
+
evicted?.type === 'event' ? evicted.name : 'pageLoad'
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
this.eventBuffer.push({
|
|
274
|
+
type: 'event',
|
|
275
|
+
name: eventName,
|
|
276
|
+
data: payload,
|
|
277
|
+
});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
this.processRecordEvent(eventName, payload);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private static processRecordEvent(eventName: string, payload: MapLike): void {
|
|
106
285
|
if (this.customerInfo) {
|
|
107
286
|
const newPayload: MapLike = {
|
|
108
287
|
...payload,
|
|
@@ -123,42 +302,36 @@ class MagicPixelImpl {
|
|
|
123
302
|
this.dl.ready();
|
|
124
303
|
}
|
|
125
304
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
305
|
+
/**
|
|
306
|
+
* Flush buffered events after SDK is ready
|
|
307
|
+
* Events are processed in the order they were received
|
|
308
|
+
*/
|
|
309
|
+
private static flushEventBuffer(): void {
|
|
310
|
+
if (this.eventBuffer.length === 0) {
|
|
311
|
+
Logger.logDbg('No buffered events to flush');
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
129
314
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
'Payload:: ',
|
|
143
|
-
item.dcrPayload
|
|
144
|
-
);
|
|
145
|
-
await this.runTM(
|
|
146
|
-
item.sseOnly,
|
|
147
|
-
item.name,
|
|
148
|
-
item.dcrName,
|
|
149
|
-
item.eventId,
|
|
150
|
-
item.dcrPayload
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
Logger.logDbg(
|
|
155
|
-
'Tag Manager is processing or is not ready. This event will start after that'
|
|
156
|
-
);
|
|
315
|
+
Logger.logDbg(`Flushing ${this.eventBuffer.length} buffered event(s)`);
|
|
316
|
+
|
|
317
|
+
// Process all buffered events in order
|
|
318
|
+
while (this.eventBuffer.length > 0) {
|
|
319
|
+
const event = this.eventBuffer.shift()!;
|
|
320
|
+
|
|
321
|
+
if (event.type === 'pageLoad') {
|
|
322
|
+
Logger.logDbg(`Processing buffered page load: ${event.data.page_name}`);
|
|
323
|
+
this.processRecordPageLoad(event.data);
|
|
324
|
+
} else if (event.type === 'event') {
|
|
325
|
+
Logger.logDbg(`Processing buffered event: ${event.name}`);
|
|
326
|
+
this.processRecordEvent(event.name!, event.data);
|
|
157
327
|
}
|
|
158
|
-
} catch (err) {
|
|
159
|
-
Logger.logError('Error check and process tm', err);
|
|
160
|
-
Reporter.reportError('m:checkAndFireTM', err);
|
|
161
328
|
}
|
|
329
|
+
|
|
330
|
+
Logger.logDbg('Event buffer flushed');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
static getDebugId(): string {
|
|
334
|
+
return DataStore.getDebugId();
|
|
162
335
|
}
|
|
163
336
|
|
|
164
337
|
private static async runTM(
|
|
@@ -178,9 +351,6 @@ class MagicPixelImpl {
|
|
|
178
351
|
eventData
|
|
179
352
|
);
|
|
180
353
|
|
|
181
|
-
// set tag manager in process status
|
|
182
|
-
DataStore.setTagManagerProcessing(true);
|
|
183
|
-
|
|
184
354
|
// process all the data elements and cache them for this run
|
|
185
355
|
await DataElementProcessor.processDataElements(
|
|
186
356
|
DataStore.getSdkDataElements(),
|
|
@@ -200,7 +370,12 @@ class MagicPixelImpl {
|
|
|
200
370
|
DataStore.setPrivacyCompliance(false);
|
|
201
371
|
}
|
|
202
372
|
} else {
|
|
203
|
-
|
|
373
|
+
// No privacy manager configured - default to true (tracking allowed)
|
|
374
|
+
Logger.logDbg(
|
|
375
|
+
'Set: PR Comp: ',
|
|
376
|
+
true,
|
|
377
|
+
'(no privacy manager configured)'
|
|
378
|
+
);
|
|
204
379
|
DataStore.setPrivacyCompliance(true);
|
|
205
380
|
}
|
|
206
381
|
|
|
@@ -271,12 +446,12 @@ class MagicPixelImpl {
|
|
|
271
446
|
): void {
|
|
272
447
|
// increment visit_depth if event name is page_load because that's the only way we can track page views in an app for now
|
|
273
448
|
if (evtName === DL_INIT_EVENT) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
});
|
|
277
|
-
} else {
|
|
278
|
-
this._fireTMPrivate(envName, envId, evtName, evtId);
|
|
449
|
+
// Synchronous increment - no race condition possible
|
|
450
|
+
VisitIdProcessor.incrementVisitDepth();
|
|
279
451
|
}
|
|
452
|
+
|
|
453
|
+
// Continue immediately after sync increment
|
|
454
|
+
this._fireTMPrivate(envName, envId, evtName, evtId);
|
|
280
455
|
}
|
|
281
456
|
|
|
282
457
|
private static _fireTMPrivate(
|
|
@@ -297,15 +472,8 @@ class MagicPixelImpl {
|
|
|
297
472
|
Reporter.reportError('l::postSST', err);
|
|
298
473
|
});
|
|
299
474
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
Utils.sleep(250).then(async () => {
|
|
303
|
-
try {
|
|
304
|
-
await this.checkAndFireTM();
|
|
305
|
-
} catch (err) {
|
|
306
|
-
Reporter.reportError('i::runTM', err);
|
|
307
|
-
}
|
|
308
|
-
});
|
|
475
|
+
// Note: EEDL handles queue progression via its own .finally() block
|
|
476
|
+
// No need to call processNext() here - it would be redundant
|
|
309
477
|
}
|
|
310
478
|
|
|
311
479
|
static setCustomerInfo(customerInfo: AppCustomerInfo): void {
|
|
@@ -321,7 +489,46 @@ class MagicPixelImpl {
|
|
|
321
489
|
this.deepLinkUrl = deepLinkUrl;
|
|
322
490
|
}
|
|
323
491
|
|
|
324
|
-
|
|
492
|
+
/**
|
|
493
|
+
* Set device info with auto-detected values
|
|
494
|
+
* Standard fields (os_version, device_model_name, package_name) are always auto-detected
|
|
495
|
+
* Optional customFields parameter allows adding custom tracking fields only
|
|
496
|
+
* @param customFields Optional custom fields to add (cannot override standard fields)
|
|
497
|
+
*/
|
|
498
|
+
static async setDeviceInfo(
|
|
499
|
+
customFields?: Record<string, any>
|
|
500
|
+
): Promise<void> {
|
|
501
|
+
// Always auto-detect standard device info (cannot be overridden)
|
|
502
|
+
const autoDetectedInfo = await DeviceInfoHelper.getAppInfo();
|
|
503
|
+
|
|
504
|
+
// Standard fields are always auto-detected to prevent hardcoded values
|
|
505
|
+
const deviceInfo: MpDeviceInfo = {
|
|
506
|
+
os_version: autoDetectedInfo.os_version,
|
|
507
|
+
device_model_name: autoDetectedInfo.device_model_name,
|
|
508
|
+
package_name: autoDetectedInfo.package_name,
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// Add any custom fields (if provided)
|
|
512
|
+
if (customFields) {
|
|
513
|
+
// Filter out attempts to override standard fields
|
|
514
|
+
const allowedCustomFields = { ...customFields };
|
|
515
|
+
delete allowedCustomFields.os_version;
|
|
516
|
+
delete allowedCustomFields.device_model_name;
|
|
517
|
+
delete allowedCustomFields.package_name;
|
|
518
|
+
|
|
519
|
+
Object.assign(deviceInfo, allowedCustomFields);
|
|
520
|
+
|
|
521
|
+
if (
|
|
522
|
+
Object.keys(allowedCustomFields).length !==
|
|
523
|
+
Object.keys(customFields).length
|
|
524
|
+
) {
|
|
525
|
+
Logger.logDbg(
|
|
526
|
+
'Warning: Attempted to override standard device fields. Using auto-detected values instead.'
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
Logger.logDbg('Setting device info:', deviceInfo);
|
|
325
532
|
DataStore.setDeviceInfo(deviceInfo);
|
|
326
533
|
}
|
|
327
534
|
|
|
@@ -336,6 +543,35 @@ class MagicPixelImpl {
|
|
|
336
543
|
}
|
|
337
544
|
|
|
338
545
|
static recordPageLoad(pageLoadInfo: AppPageLoad): void {
|
|
546
|
+
// Buffer page load if SDK is not ready yet
|
|
547
|
+
if (!this.isReady) {
|
|
548
|
+
Logger.logDbg(
|
|
549
|
+
`SDK not ready, buffering page load: ${pageLoadInfo.page_name}`
|
|
550
|
+
);
|
|
551
|
+
// Evict oldest event if buffer at capacity
|
|
552
|
+
if (this.eventBuffer.length >= MAX_BUFFERED_EVENTS) {
|
|
553
|
+
const evicted = this.eventBuffer.shift();
|
|
554
|
+
Logger.logDbg(
|
|
555
|
+
`Event buffer at capacity (${MAX_BUFFERED_EVENTS}), evicted oldest:`,
|
|
556
|
+
evicted?.type === 'event' ? evicted.name : 'pageLoad'
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
this.eventBuffer.push({
|
|
560
|
+
type: 'pageLoad',
|
|
561
|
+
data: { ...pageLoadInfo }, // Clone to preserve original data
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
this.processRecordPageLoad(pageLoadInfo);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private static processRecordPageLoad(pageLoadInfo: AppPageLoad): void {
|
|
570
|
+
// Retry visitor ID fetch if it failed during init (fire and forget)
|
|
571
|
+
VisitIdProcessor.retryVisitorIdIfNeeded().catch((err) => {
|
|
572
|
+
Logger.logError('Error retrying visitor ID fetch:', err);
|
|
573
|
+
});
|
|
574
|
+
|
|
339
575
|
pageLoadInfo.is_entry = this.firstAppLaunch ? 1 : 0;
|
|
340
576
|
|
|
341
577
|
// Use stored deepLinkUrl if it exists AND either:
|
|
@@ -345,6 +581,30 @@ class MagicPixelImpl {
|
|
|
345
581
|
pageLoadInfo.deep_link_url = this.deepLinkUrl;
|
|
346
582
|
}
|
|
347
583
|
|
|
584
|
+
// Set page_url for attribution framework
|
|
585
|
+
// Priority: deep_link_url (if present) > constructed URL (v_<orgId>://<screen_name>)
|
|
586
|
+
// Using deep_link_url allows urlp data elements to extract attribution params (gclid, fbclid, etc.)
|
|
587
|
+
if (pageLoadInfo.deep_link_url) {
|
|
588
|
+
pageLoadInfo.page_url = pageLoadInfo.deep_link_url;
|
|
589
|
+
Logger.logDbg(
|
|
590
|
+
'Setting page_url from deep_link_url:',
|
|
591
|
+
pageLoadInfo.page_url
|
|
592
|
+
);
|
|
593
|
+
} else if (this.orgId && pageLoadInfo.page_name) {
|
|
594
|
+
pageLoadInfo.page_url = `v_${this.orgId}://${pageLoadInfo.page_name}`;
|
|
595
|
+
Logger.logDbg(
|
|
596
|
+
'Setting page_url from orgId/page_name:',
|
|
597
|
+
pageLoadInfo.page_url
|
|
598
|
+
);
|
|
599
|
+
} else {
|
|
600
|
+
Logger.logDbg(
|
|
601
|
+
'Not setting page_url. No deep_link_url and orgId or page_name is missing. Org Id is: ',
|
|
602
|
+
this.orgId,
|
|
603
|
+
'page_name is: ',
|
|
604
|
+
pageLoadInfo.page_name
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
348
608
|
const deviceInfo = DataStore.getDeviceInfo();
|
|
349
609
|
|
|
350
610
|
if (this.customerInfo) {
|
|
@@ -352,7 +612,9 @@ class MagicPixelImpl {
|
|
|
352
612
|
...pageLoadInfo,
|
|
353
613
|
...this.customerInfo,
|
|
354
614
|
...(this.customerIdentifiers ?? {}),
|
|
355
|
-
|
|
615
|
+
page_params: Utils.parseQueryParamsToObject(
|
|
616
|
+
pageLoadInfo?.deep_link_url
|
|
617
|
+
),
|
|
356
618
|
};
|
|
357
619
|
|
|
358
620
|
if (deviceInfo) {
|
|
@@ -364,7 +626,9 @@ class MagicPixelImpl {
|
|
|
364
626
|
const newPayload: MapLike = {
|
|
365
627
|
...pageLoadInfo,
|
|
366
628
|
...(this.customerIdentifiers ?? {}),
|
|
367
|
-
|
|
629
|
+
page_params: Utils.parseQueryParamsToObject(
|
|
630
|
+
pageLoadInfo?.deep_link_url
|
|
631
|
+
),
|
|
368
632
|
};
|
|
369
633
|
|
|
370
634
|
if (deviceInfo) {
|
|
@@ -461,3 +725,4 @@ class MagicPixelImpl {
|
|
|
461
725
|
|
|
462
726
|
export const MagicPixelEventBus = EventBus;
|
|
463
727
|
export const MagicPixel = MagicPixelImpl;
|
|
728
|
+
export { DeepLinkHelper, DeepLinkType } from './common/deeplink-helper';
|
|
@@ -10,6 +10,14 @@ export interface ClientSdkDeItem {
|
|
|
10
10
|
fn?: (deHelper: any, input: any) => any;
|
|
11
11
|
isAsync?: boolean;
|
|
12
12
|
fnTOut?: number;
|
|
13
|
+
stDur?: DeStorageDuration; // storage duration
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export enum DeStorageDuration {
|
|
17
|
+
NONE = 'NONE',
|
|
18
|
+
EVENT = 'EVENT',
|
|
19
|
+
SESSION = 'SESSION',
|
|
20
|
+
VISITOR = 'VISITOR',
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
export enum ParamResourceEncodingType {
|
|
@@ -4,13 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Utils } from '../common/utils';
|
|
6
6
|
import type { DeJsvHelper, MapLike } from '../common/app-types';
|
|
7
|
+
import { DeStorageDuration } from '../models/mp-client-sdk';
|
|
7
8
|
import type { ClientSdkDeItem } from '../models/mp-client-sdk';
|
|
8
9
|
import { DataStore } from '../common/data-store';
|
|
9
10
|
import { Logger } from '../common/logger';
|
|
11
|
+
import { StorageHelper } from '../common/storage-helper';
|
|
10
12
|
|
|
11
13
|
// Note: JSV (JavaScript functions) are not supported in React Native
|
|
12
14
|
// This timeout function is kept for potential future use
|
|
13
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Check if a value is defined (not null, undefined, or empty string)
|
|
18
|
+
*/
|
|
19
|
+
function isDefined(value: any): boolean {
|
|
20
|
+
return value !== null && value !== undefined && value !== '';
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
const dElemProcessors: MapLike<
|
|
15
24
|
(
|
|
16
25
|
helper: DeJsvHelper,
|
|
@@ -86,7 +95,10 @@ const dElemProcessors: MapLike<
|
|
|
86
95
|
_elem: ClientSdkDeItem,
|
|
87
96
|
_eventData?: Record<string, any>
|
|
88
97
|
): any => {
|
|
89
|
-
|
|
98
|
+
// Visit depth should start at 1 (first page), not 0
|
|
99
|
+
// Return at least 1 to ensure first page view shows depth = 1
|
|
100
|
+
const depth = DataStore.getVisitInfo()?.visitDepth ?? 0;
|
|
101
|
+
return Math.max(1, depth);
|
|
90
102
|
},
|
|
91
103
|
/* Browser/Platform data elements */
|
|
92
104
|
pg_browser: (
|
|
@@ -96,14 +108,13 @@ const dElemProcessors: MapLike<
|
|
|
96
108
|
): any => {
|
|
97
109
|
return DataStore.getDeviceOs(); // Using device OS as browser equivalent
|
|
98
110
|
},
|
|
99
|
-
/* User status data element -
|
|
111
|
+
/* User status data element - 1 = new visitor (first visit), 0 = returning visitor */
|
|
100
112
|
mp_new_vis: (
|
|
101
113
|
_helper: DeJsvHelper,
|
|
102
114
|
_elem: ClientSdkDeItem,
|
|
103
115
|
_eventData?: Record<string, any>
|
|
104
116
|
): any => {
|
|
105
|
-
|
|
106
|
-
return '1'; // Default to new visitor
|
|
117
|
+
return DataStore.getMpUserStatus();
|
|
107
118
|
},
|
|
108
119
|
pg_random: (
|
|
109
120
|
_helper: DeJsvHelper,
|
|
@@ -178,6 +189,55 @@ const dElemProcessors: MapLike<
|
|
|
178
189
|
_elem.key = 'ev._eventName';
|
|
179
190
|
return processMpDl(_helper, _elem, _eventData);
|
|
180
191
|
},
|
|
192
|
+
pg_hash: (
|
|
193
|
+
_helper: DeJsvHelper,
|
|
194
|
+
_elem: ClientSdkDeItem,
|
|
195
|
+
_eventData?: Record<string, any>
|
|
196
|
+
): any => {
|
|
197
|
+
return '';
|
|
198
|
+
},
|
|
199
|
+
pg_title: (
|
|
200
|
+
_helper: DeJsvHelper,
|
|
201
|
+
_elem: ClientSdkDeItem,
|
|
202
|
+
_eventData?: Record<string, any>
|
|
203
|
+
): any => {
|
|
204
|
+
return '';
|
|
205
|
+
},
|
|
206
|
+
pg_params: (
|
|
207
|
+
_helper: DeJsvHelper,
|
|
208
|
+
_elem: ClientSdkDeItem,
|
|
209
|
+
_eventData?: Record<string, any>
|
|
210
|
+
): any => {
|
|
211
|
+
return '';
|
|
212
|
+
},
|
|
213
|
+
pg_domain: (
|
|
214
|
+
_helper: DeJsvHelper,
|
|
215
|
+
_elem: ClientSdkDeItem,
|
|
216
|
+
_eventData?: Record<string, any>
|
|
217
|
+
): any => {
|
|
218
|
+
return '';
|
|
219
|
+
},
|
|
220
|
+
pg_srch: (
|
|
221
|
+
_helper: DeJsvHelper,
|
|
222
|
+
_elem: ClientSdkDeItem,
|
|
223
|
+
_eventData?: Record<string, any>
|
|
224
|
+
): any => {
|
|
225
|
+
return '';
|
|
226
|
+
},
|
|
227
|
+
pg_referrer: (
|
|
228
|
+
_helper: DeJsvHelper,
|
|
229
|
+
_elem: ClientSdkDeItem,
|
|
230
|
+
_eventData?: Record<string, any>
|
|
231
|
+
): any => {
|
|
232
|
+
return '';
|
|
233
|
+
},
|
|
234
|
+
pg_url: (
|
|
235
|
+
_helper: DeJsvHelper,
|
|
236
|
+
_elem: ClientSdkDeItem,
|
|
237
|
+
_eventData?: Record<string, any>
|
|
238
|
+
): any => {
|
|
239
|
+
return '';
|
|
240
|
+
},
|
|
181
241
|
// Note: JSV (JavaScript functions) processor removed - not supported in React Native
|
|
182
242
|
};
|
|
183
243
|
|
|
@@ -238,14 +298,32 @@ export async function processOneDataElement(
|
|
|
238
298
|
try {
|
|
239
299
|
if (dElemProcessors[deItem.typ]) {
|
|
240
300
|
Logger.logDbg('Processing data element: ', deItem.nm);
|
|
241
|
-
|
|
301
|
+
let deValue = await dElemProcessors[deItem.typ](
|
|
242
302
|
deHelper,
|
|
243
303
|
deItem,
|
|
244
304
|
eventData
|
|
245
305
|
);
|
|
246
306
|
|
|
247
|
-
//
|
|
248
|
-
|
|
307
|
+
// Default storage duration to EVENT if not defined
|
|
308
|
+
if (!deItem.stDur) {
|
|
309
|
+
deItem.stDur = DeStorageDuration.EVENT;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// If data element has persistence enabled, store the value
|
|
313
|
+
StorageHelper.deStorageDurationSet(deItem.key, deValue, deItem.stDur);
|
|
314
|
+
|
|
315
|
+
// If value is not defined, try to retrieve from storage
|
|
316
|
+
// This allows values like gclid to persist across page loads
|
|
317
|
+
if (!isDefined(deValue)) {
|
|
318
|
+
const storedValue = StorageHelper.deStorageDurationGet(
|
|
319
|
+
deItem.key,
|
|
320
|
+
deItem.def,
|
|
321
|
+
deItem.stDur
|
|
322
|
+
);
|
|
323
|
+
if (isDefined(storedValue)) {
|
|
324
|
+
deValue = storedValue;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
249
327
|
|
|
250
328
|
return deValue;
|
|
251
329
|
} else {
|