@magicpixel/rn-mp-client-sdk 1.13.0 → 1.13.21

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.
Files changed (104) hide show
  1. package/README.md +163 -14
  2. package/lib/commonjs/common/app-types.js.map +1 -1
  3. package/lib/commonjs/common/constants.js +11 -2
  4. package/lib/commonjs/common/constants.js.map +1 -1
  5. package/lib/commonjs/common/data-store.js +13 -30
  6. package/lib/commonjs/common/data-store.js.map +1 -1
  7. package/lib/commonjs/common/deeplink-helper.js +174 -0
  8. package/lib/commonjs/common/deeplink-helper.js.map +1 -0
  9. package/lib/commonjs/common/device-info-helper.js +168 -0
  10. package/lib/commonjs/common/device-info-helper.js.map +1 -0
  11. package/lib/commonjs/common/event-bus.js +39 -0
  12. package/lib/commonjs/common/event-bus.js.map +1 -1
  13. package/lib/commonjs/common/network-service.js +119 -15
  14. package/lib/commonjs/common/network-service.js.map +1 -1
  15. package/lib/commonjs/common/reporter.js +75 -14
  16. package/lib/commonjs/common/reporter.js.map +1 -1
  17. package/lib/commonjs/common/storage-helper.js +227 -0
  18. package/lib/commonjs/common/storage-helper.js.map +1 -0
  19. package/lib/commonjs/common/utils.js +62 -2
  20. package/lib/commonjs/common/utils.js.map +1 -1
  21. package/lib/commonjs/eedl/eedl.js +198 -44
  22. package/lib/commonjs/eedl/eedl.js.map +1 -1
  23. package/lib/commonjs/index.js +301 -54
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/models/mp-client-sdk.js +17 -10
  26. package/lib/commonjs/models/mp-client-sdk.js.map +1 -1
  27. package/lib/commonjs/processors/data-element.processor.js +51 -7
  28. package/lib/commonjs/processors/data-element.processor.js.map +1 -1
  29. package/lib/commonjs/processors/visit-id.processor.js +78 -15
  30. package/lib/commonjs/processors/visit-id.processor.js.map +1 -1
  31. package/lib/module/common/app-types.js.map +1 -1
  32. package/lib/module/common/constants.js +11 -2
  33. package/lib/module/common/constants.js.map +1 -1
  34. package/lib/module/common/data-store.js +13 -30
  35. package/lib/module/common/data-store.js.map +1 -1
  36. package/lib/module/common/deeplink-helper.js +168 -0
  37. package/lib/module/common/deeplink-helper.js.map +1 -0
  38. package/lib/module/common/device-info-helper.js +161 -0
  39. package/lib/module/common/device-info-helper.js.map +1 -0
  40. package/lib/module/common/event-bus.js +39 -0
  41. package/lib/module/common/event-bus.js.map +1 -1
  42. package/lib/module/common/network-service.js +119 -15
  43. package/lib/module/common/network-service.js.map +1 -1
  44. package/lib/module/common/reporter.js +76 -14
  45. package/lib/module/common/reporter.js.map +1 -1
  46. package/lib/module/common/storage-helper.js +221 -0
  47. package/lib/module/common/storage-helper.js.map +1 -0
  48. package/lib/module/common/utils.js +63 -2
  49. package/lib/module/common/utils.js.map +1 -1
  50. package/lib/module/eedl/eedl.js +198 -44
  51. package/lib/module/eedl/eedl.js.map +1 -1
  52. package/lib/module/index.js +290 -53
  53. package/lib/module/index.js.map +1 -1
  54. package/lib/module/models/mp-client-sdk.js +16 -9
  55. package/lib/module/models/mp-client-sdk.js.map +1 -1
  56. package/lib/module/processors/data-element.processor.js +51 -7
  57. package/lib/module/processors/data-element.processor.js.map +1 -1
  58. package/lib/module/processors/visit-id.processor.js +78 -15
  59. package/lib/module/processors/visit-id.processor.js.map +1 -1
  60. package/lib/typescript/{common → src/common}/app-types.d.ts +30 -9
  61. package/lib/typescript/{common → src/common}/constants.d.ts +0 -1
  62. package/lib/typescript/{common → src/common}/data-store.d.ts +3 -8
  63. package/lib/typescript/src/common/deeplink-helper.d.ts +60 -0
  64. package/lib/typescript/src/common/device-info-helper.d.ts +54 -0
  65. package/lib/typescript/src/common/event-bus.d.ts +21 -0
  66. package/lib/typescript/src/common/network-service.d.ts +32 -0
  67. package/lib/typescript/{common → src/common}/reporter.d.ts +2 -1
  68. package/lib/typescript/src/common/storage-helper.d.ts +47 -0
  69. package/lib/typescript/{common → src/common}/utils.d.ts +25 -0
  70. package/lib/typescript/{eedl → src/eedl}/eedl.d.ts +43 -1
  71. package/lib/typescript/{index.d.ts → src/index.d.ts} +39 -5
  72. package/lib/typescript/{models → src/models}/mp-client-sdk.d.ts +7 -0
  73. package/lib/typescript/src/processors/visit-id.processor.d.ts +23 -0
  74. package/package.json +25 -36
  75. package/src/common/app-types.ts +33 -10
  76. package/src/common/constants.ts +0 -6
  77. package/src/common/data-store.ts +8 -30
  78. package/src/common/deeplink-helper.ts +181 -0
  79. package/src/common/device-info-helper.ts +190 -0
  80. package/src/common/event-bus.ts +39 -0
  81. package/src/common/network-service.ts +154 -21
  82. package/src/common/reporter.ts +97 -16
  83. package/src/common/storage-helper.ts +260 -0
  84. package/src/common/utils.ts +63 -2
  85. package/src/eedl/eedl.ts +225 -51
  86. package/src/index.tsx +346 -73
  87. package/src/models/mp-client-sdk.ts +8 -0
  88. package/src/processors/data-element.processor.ts +85 -7
  89. package/src/processors/visit-id.processor.ts +92 -22
  90. package/lib/commonjs/processors/trans-function.processor.js +0 -73
  91. package/lib/commonjs/processors/trans-function.processor.js.map +0 -1
  92. package/lib/module/processors/trans-function.processor.js +0 -66
  93. package/lib/module/processors/trans-function.processor.js.map +0 -1
  94. package/lib/typescript/common/event-bus.d.ts +0 -6
  95. package/lib/typescript/common/network-service.d.ts +0 -8
  96. package/lib/typescript/processors/trans-function.processor.d.ts +0 -12
  97. package/lib/typescript/processors/visit-id.processor.d.ts +0 -9
  98. package/src/processors/trans-function.processor.ts +0 -85
  99. /package/lib/typescript/{common → src/common}/logger.d.ts +0 -0
  100. /package/lib/typescript/{models → src/models}/geo-api-response.d.ts +0 -0
  101. /package/lib/typescript/{processors → src/processors}/data-element.processor.d.ts +0 -0
  102. /package/lib/typescript/{processors → src/processors}/geo-location.processor.d.ts +0 -0
  103. /package/lib/typescript/{processors → src/processors}/qc.processor.d.ts +0 -0
  104. /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
- static async init(options: SdkInitOptions): Promise<void> {
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('MagicPixel SDK is not ready. No tags will be processed');
151
+ throw new Error(
152
+ 'MagicPixel SDK: DataStore not ready after config fetch. Initialization failed.'
153
+ );
43
154
  }
44
155
 
45
- if (
46
- options &&
47
- options.device_type &&
48
- options.device_type?.trim().length > 0
49
- ) {
50
- DataStore.overrideDeviceType(options.device_type);
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);
52
163
 
53
- this.setAppVersion(options.app_version);
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);
169
+
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
- // if this is something the tag manager is interested in - fire tag manager
85
- DataStore.enQueueTMFire(
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
- static getDebugId(): string {
127
- return DataStore.getDebugId();
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
- private static async checkAndFireTM(): Promise<void> {
131
- try {
132
- if (!DataStore.isTagManagerProcessing()) {
133
- const item = DataStore.deQueueTMFire();
134
- if (item) {
135
- Logger.logDbg(
136
- 'Executing TM For: ',
137
- item.name,
138
- ', Event:: ',
139
- item.dcrName,
140
- ', Event id:: ',
141
- item.eventId,
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
- Logger.logDbg('Set: PR Comp: ', false);
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
 
@@ -236,6 +411,11 @@ class MagicPixelImpl {
236
411
  []
237
412
  );
238
413
 
414
+ // Store developer payload (base64 encoded) for report
415
+ if (eventData) {
416
+ Reporter.reportDevPayload(eventData, evtId);
417
+ }
418
+
239
419
  const validQCList = QcProcessor.processQc(
240
420
  DataStore.getSdkQC(),
241
421
  evtName,
@@ -247,15 +427,15 @@ class MagicPixelImpl {
247
427
 
248
428
  TagProcessor.processTags(evtName, evtId)
249
429
  .then(() => {
250
- this._fireTM(sdk.s.ev, sdk.s.ev_id, evtName, evtId);
430
+ this._fireTM(sdk.s.ev, sdk.s.ev_id, evtName, evtId, eventData);
251
431
  })
252
432
  .catch((err) => {
253
433
  Logger.logError('Error processing tag lists.', err);
254
434
  Reporter.reportError('i::processTags', err);
255
- this._fireTM(sdk.s.ev, sdk.s.ev_id, evtName, evtId);
435
+ this._fireTM(sdk.s.ev, sdk.s.ev_id, evtName, evtId, eventData);
256
436
  });
257
437
  } else {
258
- this._fireTM(sdk.s.ev, sdk.s.ev_id, evtName, evtId);
438
+ this._fireTM(sdk.s.ev, sdk.s.ev_id, evtName, evtId, eventData);
259
439
  }
260
440
  } catch (runTMErr) {
261
441
  Logger.logError('Error in runTM', runTMErr);
@@ -267,23 +447,25 @@ class MagicPixelImpl {
267
447
  envName: string,
268
448
  envId: string,
269
449
  evtName: string,
270
- evtId: string
450
+ evtId: string,
451
+ eventData?: Record<string, any>
271
452
  ): void {
272
453
  // 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
454
  if (evtName === DL_INIT_EVENT) {
274
- VisitIdProcessor.incrementVisitDepth().finally(() => {
275
- this._fireTMPrivate(envName, envId, evtName, evtId);
276
- });
277
- } else {
278
- this._fireTMPrivate(envName, envId, evtName, evtId);
455
+ // Synchronous increment - no race condition possible
456
+ VisitIdProcessor.incrementVisitDepth();
279
457
  }
458
+
459
+ // Continue immediately after sync increment
460
+ this._fireTMPrivate(envName, envId, evtName, evtId, eventData);
280
461
  }
281
462
 
282
463
  private static _fireTMPrivate(
283
464
  envName: string,
284
465
  envId: string,
285
466
  evtName: string,
286
- evtId: string
467
+ evtId: string,
468
+ eventData?: Record<string, any>
287
469
  ): void {
288
470
  if (DataStore.hasOneSSTTag() && DataStore.shouldFireSstForEvent(evtName)) {
289
471
  Reporter.postSST(
@@ -292,20 +474,14 @@ class MagicPixelImpl {
292
474
  envId,
293
475
  DataStore.getSSTDownStream(),
294
476
  evtName,
295
- evtId
477
+ evtId,
478
+ eventData
296
479
  ).catch((err) => {
297
480
  Reporter.reportError('l::postSST', err);
298
481
  });
299
482
  }
300
-
301
- DataStore.setTagManagerProcessing(false);
302
- Utils.sleep(250).then(async () => {
303
- try {
304
- await this.checkAndFireTM();
305
- } catch (err) {
306
- Reporter.reportError('i::runTM', err);
307
- }
308
- });
483
+ // Note: EEDL handles queue progression via its own .finally() block
484
+ // No need to call processNext() here - it would be redundant
309
485
  }
310
486
 
311
487
  static setCustomerInfo(customerInfo: AppCustomerInfo): void {
@@ -321,7 +497,46 @@ class MagicPixelImpl {
321
497
  this.deepLinkUrl = deepLinkUrl;
322
498
  }
323
499
 
324
- static setDeviceInfo(deviceInfo: MpDeviceInfo): void {
500
+ /**
501
+ * Set device info with auto-detected values
502
+ * Standard fields (os_version, device_model_name, package_name) are always auto-detected
503
+ * Optional customFields parameter allows adding custom tracking fields only
504
+ * @param customFields Optional custom fields to add (cannot override standard fields)
505
+ */
506
+ static async setDeviceInfo(
507
+ customFields?: Record<string, any>
508
+ ): Promise<void> {
509
+ // Always auto-detect standard device info (cannot be overridden)
510
+ const autoDetectedInfo = await DeviceInfoHelper.getAppInfo();
511
+
512
+ // Standard fields are always auto-detected to prevent hardcoded values
513
+ const deviceInfo: MpDeviceInfo = {
514
+ os_version: autoDetectedInfo.os_version,
515
+ device_model_name: autoDetectedInfo.device_model_name,
516
+ package_name: autoDetectedInfo.package_name,
517
+ };
518
+
519
+ // Add any custom fields (if provided)
520
+ if (customFields) {
521
+ // Filter out attempts to override standard fields
522
+ const allowedCustomFields = { ...customFields };
523
+ delete allowedCustomFields.os_version;
524
+ delete allowedCustomFields.device_model_name;
525
+ delete allowedCustomFields.package_name;
526
+
527
+ Object.assign(deviceInfo, allowedCustomFields);
528
+
529
+ if (
530
+ Object.keys(allowedCustomFields).length !==
531
+ Object.keys(customFields).length
532
+ ) {
533
+ Logger.logDbg(
534
+ 'Warning: Attempted to override standard device fields. Using auto-detected values instead.'
535
+ );
536
+ }
537
+ }
538
+
539
+ Logger.logDbg('Setting device info:', deviceInfo);
325
540
  DataStore.setDeviceInfo(deviceInfo);
326
541
  }
327
542
 
@@ -336,6 +551,35 @@ class MagicPixelImpl {
336
551
  }
337
552
 
338
553
  static recordPageLoad(pageLoadInfo: AppPageLoad): void {
554
+ // Buffer page load if SDK is not ready yet
555
+ if (!this.isReady) {
556
+ Logger.logDbg(
557
+ `SDK not ready, buffering page load: ${pageLoadInfo.page_name}`
558
+ );
559
+ // Evict oldest event if buffer at capacity
560
+ if (this.eventBuffer.length >= MAX_BUFFERED_EVENTS) {
561
+ const evicted = this.eventBuffer.shift();
562
+ Logger.logDbg(
563
+ `Event buffer at capacity (${MAX_BUFFERED_EVENTS}), evicted oldest:`,
564
+ evicted?.type === 'event' ? evicted.name : 'pageLoad'
565
+ );
566
+ }
567
+ this.eventBuffer.push({
568
+ type: 'pageLoad',
569
+ data: { ...pageLoadInfo }, // Clone to preserve original data
570
+ });
571
+ return;
572
+ }
573
+
574
+ this.processRecordPageLoad(pageLoadInfo);
575
+ }
576
+
577
+ private static processRecordPageLoad(pageLoadInfo: AppPageLoad): void {
578
+ // Retry visitor ID fetch if it failed during init (fire and forget)
579
+ VisitIdProcessor.retryVisitorIdIfNeeded().catch((err) => {
580
+ Logger.logError('Error retrying visitor ID fetch:', err);
581
+ });
582
+
339
583
  pageLoadInfo.is_entry = this.firstAppLaunch ? 1 : 0;
340
584
 
341
585
  // Use stored deepLinkUrl if it exists AND either:
@@ -345,6 +589,30 @@ class MagicPixelImpl {
345
589
  pageLoadInfo.deep_link_url = this.deepLinkUrl;
346
590
  }
347
591
 
592
+ // Set page_url for attribution framework
593
+ // Priority: deep_link_url (if present) > constructed URL (v_<orgId>://<screen_name>)
594
+ // Using deep_link_url allows urlp data elements to extract attribution params (gclid, fbclid, etc.)
595
+ if (pageLoadInfo.deep_link_url) {
596
+ pageLoadInfo.page_url = pageLoadInfo.deep_link_url;
597
+ Logger.logDbg(
598
+ 'Setting page_url from deep_link_url:',
599
+ pageLoadInfo.page_url
600
+ );
601
+ } else if (this.orgId && pageLoadInfo.page_name) {
602
+ pageLoadInfo.page_url = `v_${this.orgId}://${pageLoadInfo.page_name}`;
603
+ Logger.logDbg(
604
+ 'Setting page_url from orgId/page_name:',
605
+ pageLoadInfo.page_url
606
+ );
607
+ } else {
608
+ Logger.logDbg(
609
+ 'Not setting page_url. No deep_link_url and orgId or page_name is missing. Org Id is: ',
610
+ this.orgId,
611
+ 'page_name is: ',
612
+ pageLoadInfo.page_name
613
+ );
614
+ }
615
+
348
616
  const deviceInfo = DataStore.getDeviceInfo();
349
617
 
350
618
  if (this.customerInfo) {
@@ -352,7 +620,9 @@ class MagicPixelImpl {
352
620
  ...pageLoadInfo,
353
621
  ...this.customerInfo,
354
622
  ...(this.customerIdentifiers ?? {}),
355
- ...Utils.parseQueryParamsToObject(pageLoadInfo?.deep_link_url),
623
+ page_params: Utils.parseQueryParamsToObject(
624
+ pageLoadInfo?.deep_link_url
625
+ ),
356
626
  };
357
627
 
358
628
  if (deviceInfo) {
@@ -364,7 +634,9 @@ class MagicPixelImpl {
364
634
  const newPayload: MapLike = {
365
635
  ...pageLoadInfo,
366
636
  ...(this.customerIdentifiers ?? {}),
367
- ...Utils.parseQueryParamsToObject(pageLoadInfo?.deep_link_url),
637
+ page_params: Utils.parseQueryParamsToObject(
638
+ pageLoadInfo?.deep_link_url
639
+ ),
368
640
  };
369
641
 
370
642
  if (deviceInfo) {
@@ -461,3 +733,4 @@ class MagicPixelImpl {
461
733
 
462
734
  export const MagicPixelEventBus = EventBus;
463
735
  export const MagicPixel = MagicPixelImpl;
736
+ 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 {