@datalyr/react-native 1.7.5 → 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalyr/react-native",
3
- "version": "1.7.5",
3
+ "version": "1.7.6",
4
4
  "description": "Datalyr SDK for React Native & Expo - Server-side attribution tracking for iOS and Android",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -206,6 +206,11 @@ export class DatalyrSDKExpo {
206
206
  }
207
207
 
208
208
  if (attributionManager.isInstall()) {
209
+ // iOS: Attempt deferred web-to-app attribution via IP matching before tracking install
210
+ if (Platform.OS === 'ios') {
211
+ await this.fetchDeferredWebAttribution();
212
+ }
213
+
209
214
  const installData = await attributionManager.trackInstall();
210
215
  await this.track('app_install', {
211
216
  platform: Platform.OS,
@@ -376,6 +381,87 @@ export class DatalyrSDKExpo {
376
381
  }
377
382
  }
378
383
 
384
+ /**
385
+ * Fetch deferred web attribution on first app install.
386
+ * Uses IP-based matching to recover attribution data (fbclid, utm_*, etc.)
387
+ * from a prelander web visit. Called automatically during initialize()
388
+ * when a fresh install is detected on iOS.
389
+ */
390
+ private async fetchDeferredWebAttribution(): Promise<void> {
391
+ if (!this.state.config?.apiKey) {
392
+ debugLog('API key not available for deferred attribution fetch');
393
+ return;
394
+ }
395
+
396
+ try {
397
+ debugLog('Fetching deferred web attribution via IP matching...');
398
+
399
+ const controller = new AbortController();
400
+ const timeout = setTimeout(() => controller.abort(), 10000);
401
+
402
+ const response = await fetch('https://api.datalyr.com/attribution/deferred-lookup', {
403
+ method: 'POST',
404
+ headers: {
405
+ 'Content-Type': 'application/json',
406
+ 'X-Datalyr-API-Key': this.state.config.apiKey!,
407
+ },
408
+ body: JSON.stringify({ platform: Platform.OS }),
409
+ signal: controller.signal,
410
+ });
411
+
412
+ clearTimeout(timeout);
413
+
414
+ if (!response.ok) {
415
+ debugLog('Deferred attribution lookup failed:', response.status);
416
+ return;
417
+ }
418
+
419
+ const result = await response.json() as { found: boolean; attribution?: any };
420
+
421
+ if (!result.found || !result.attribution) {
422
+ debugLog('No deferred web attribution found for this IP');
423
+ return;
424
+ }
425
+
426
+ const webAttribution = result.attribution;
427
+ debugLog('Deferred web attribution found:', {
428
+ visitor_id: webAttribution.visitor_id,
429
+ has_fbclid: !!webAttribution.fbclid,
430
+ has_gclid: !!webAttribution.gclid,
431
+ utm_source: webAttribution.utm_source,
432
+ });
433
+
434
+ // Merge web attribution into current session
435
+ attributionManager.mergeWebAttribution(webAttribution);
436
+
437
+ // Track match event for analytics
438
+ await this.track('$web_attribution_matched', {
439
+ web_visitor_id: webAttribution.visitor_id,
440
+ web_user_id: webAttribution.user_id,
441
+ fbclid: webAttribution.fbclid,
442
+ gclid: webAttribution.gclid,
443
+ ttclid: webAttribution.ttclid,
444
+ gbraid: webAttribution.gbraid,
445
+ wbraid: webAttribution.wbraid,
446
+ fbp: webAttribution.fbp,
447
+ fbc: webAttribution.fbc,
448
+ utm_source: webAttribution.utm_source,
449
+ utm_medium: webAttribution.utm_medium,
450
+ utm_campaign: webAttribution.utm_campaign,
451
+ utm_content: webAttribution.utm_content,
452
+ utm_term: webAttribution.utm_term,
453
+ web_timestamp: webAttribution.timestamp,
454
+ match_method: 'ip',
455
+ });
456
+
457
+ debugLog('Successfully merged deferred web attribution');
458
+
459
+ } catch (error) {
460
+ errorLog('Error fetching deferred web attribution:', error as Error);
461
+ // Non-blocking - email-based fallback will catch this on identify()
462
+ }
463
+ }
464
+
379
465
  async alias(newUserId: string, previousId?: string): Promise<void> {
380
466
  try {
381
467
  if (!newUserId || typeof newUserId !== 'string') {