@advenue/react-native 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,6 +33,11 @@ await Advenue.initialize({
33
33
  Advenue.track('signup_completed', { plan: 'pro' });
34
34
  ```
35
35
 
36
+ > The `adv_` event-name prefix (e.g. `adv_meta_aem`) is reserved for
37
+ > Advenue-internal signals — don't name your own custom events with it. Such
38
+ > events are still accepted by `track()` (the pipeline relies on them) but are
39
+ > hidden from analytics surfaces like the dashboard's funnel event picker.
40
+
36
41
  ## Deep links (Universal Links / App Links)
37
42
 
38
43
  Add the config plugin so install-time deep links resolve on device:
@@ -215,10 +215,12 @@ class AdvenueAndroidModule : Module() {
215
215
 
216
216
  // Play Integrity Standard API — device attestation (G3.1n).
217
217
  //
218
- // `requestHash` MUST be sha256_hex("<appId>:<deviceId>") — lowercase hex of
219
- // SHA-256 over the literal string "<appId>:<deviceId>". The server's
220
- // mapIntegrityVerdict (Task 3) re-derives the same string and compares it to
221
- // the token's requestHash field; the strings must match byte-for-byte.
218
+ // `requestHash` MUST be sha256_hex("<deviceId>") — lowercase hex of SHA-256
219
+ // over the device id alone. The server's mapIntegrityVerdict re-derives the
220
+ // same string and compares it to the token's requestHash field; the strings
221
+ // must match byte-for-byte. No app id is hashed in: the token is decoded
222
+ // against this app's Play package name and must be PLAY_RECOGNIZED, which
223
+ // binds the app already.
222
224
  //
223
225
  // The provider is warmed in OnCreate (above). If it is null — because the
224
226
  // manifest meta-data key is absent, Play Integrity is unsupported, or warming
@@ -284,6 +286,25 @@ class AdvenueAndroidModule : Module() {
284
286
 
285
287
  val ctx = appContext.reactContext?.applicationContext
286
288
  if (ctx != null) {
289
+ // Meta CAPI extinfo slots 1-3. versionName is the user-facing version
290
+ // (slot 2, surfaced as the event's appVersion); the version code is the
291
+ // build identifier (slot 3). Best-effort: a missing PackageInfo must
292
+ // not cost the caller the rest of the device metadata.
293
+ map["packageName"] = ctx.packageName
294
+ try {
295
+ val pi = ctx.packageManager.getPackageInfo(ctx.packageName, 0)
296
+ pi.versionName?.let { map["shortVersion"] = it }
297
+ @Suppress("DEPRECATION")
298
+ val code =
299
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
300
+ pi.longVersionCode
301
+ } else {
302
+ pi.versionCode.toLong()
303
+ }
304
+ map["longVersion"] = code.toString()
305
+ } catch (_: Exception) {
306
+ // package info best-effort
307
+ }
287
308
  try {
288
309
  val dm = ctx.resources.displayMetrics
289
310
  map["screenWidth"] = dm.widthPixels
@@ -327,6 +348,29 @@ class AdvenueAndroidModule : Module() {
327
348
  sdkPrefs()?.edit()?.remove(key)?.apply()
328
349
  }
329
350
 
351
+ // IAB TCF v2 data a TCF-compliant CMP (e.g. Google UMP) writes to the
352
+ // *default* SharedPreferences ("<package>_preferences", per the TCF spec) —
353
+ // deliberately not our io.advenue.sdk file. Raw values pass through; the
354
+ // TS side (tcfToConsent) owns the policy mapping.
355
+ Function("getTcfData") { ->
356
+ val ctx = appContext.reactContext?.applicationContext ?: return@Function null
357
+ val prefs = ctx.getSharedPreferences(
358
+ "${ctx.packageName}_preferences", android.content.Context.MODE_PRIVATE
359
+ )
360
+ // prefs.all + map lookup: a missing key reads as null (no default value
361
+ // involved), so "missing" and "0" stay distinguishable. `as? Number`
362
+ // tolerates a CMP writing the value as Int OR Long (SharedPreferences
363
+ // has no fixed numeric type per the TCF spec's own contract) without
364
+ // degrading a Long writer to unknown. runCatching stays as the
365
+ // last-resort crash guard.
366
+ val gdprApplies = runCatching {
367
+ (prefs.all["IABTCF_gdprApplies"] as? Number)?.toInt()
368
+ }.getOrNull()
369
+ val purposeConsents = runCatching { prefs.getString("IABTCF_PurposeConsents", null) }.getOrNull()
370
+ if (gdprApplies == null && purposeConsents == null) return@Function null
371
+ mapOf("gdprApplies" to gdprApplies, "purposeConsents" to purposeConsents)
372
+ }
373
+
330
374
  // SSAID (ANDROID_ID): signing-key-scoped, stable across reinstalls, reset
331
375
  // only by factory reset. Read by the JS facade ONLY when no usable GAID
332
376
  // exists, as a first-party fraud/dedup + reinstall-detection signal (Play
package/dist/index.cjs CHANGED
@@ -63,6 +63,7 @@ __export(index_exports, {
63
63
  getIosNative: () => getIosNative,
64
64
  parseDirectLink: () => parseDirectLink,
65
65
  resolveAdvertisingId: () => resolveAdvertisingId,
66
+ resolveDmaConsent: () => resolveDmaConsent,
66
67
  trackInstall: () => trackInstall
67
68
  });
68
69
  module.exports = __toCommonJS(index_exports);
@@ -221,7 +222,7 @@ function sha256Hex(message) {
221
222
  // package.json
222
223
  var package_default = {
223
224
  name: "@advenue/react-native",
224
- version: "0.7.0",
225
+ version: "0.9.0",
225
226
  description: "Advenue MMP React Native SDK \u2014 attribution, install tracking, and deep links (bundled iOS/Android Expo native modules)",
226
227
  type: "module",
227
228
  license: "MIT",
@@ -282,6 +283,17 @@ var package_default = {
282
283
  "react-native": "./dist/index.cjs"
283
284
  };
284
285
 
286
+ // src/aem.ts
287
+ function extractAemCampaignIds(alApplinkData) {
288
+ try {
289
+ const parsed = JSON.parse(alApplinkData);
290
+ const value = typeof parsed.campaign_ids === "string" && parsed.campaign_ids ? parsed.campaign_ids : typeof parsed.extras?.campaign_ids === "string" && parsed.extras.campaign_ids ? parsed.extras.campaign_ids : null;
291
+ return value && value.length <= 2048 ? value : null;
292
+ } catch {
293
+ return null;
294
+ }
295
+ }
296
+
285
297
  // src/deep-links.ts
286
298
  function parseDirectLink(url) {
287
299
  let deepLinkValue = "";
@@ -307,11 +319,16 @@ function parseDirectLink(url) {
307
319
  };
308
320
  }
309
321
  function setupDeepLinks(setup) {
310
- const listener = setup.addUrlListener((url) => setup.onDeepLink(parseDirectLink(url)));
322
+ const deliverDirect = (url) => {
323
+ const link = parseDirectLink(url);
324
+ setup.onDirectUrl?.(url, link);
325
+ setup.onDeepLink(link);
326
+ };
327
+ const listener = setup.addUrlListener(deliverDirect);
311
328
  const ready = (async () => {
312
329
  const initial = await setup.getInitialURL();
313
330
  if (initial) {
314
- setup.onDeepLink(parseDirectLink(initial));
331
+ deliverDirect(initial);
315
332
  return;
316
333
  }
317
334
  const deferred = await setup.resolveDeferred();
@@ -458,7 +475,29 @@ async function resolveAdvertisingId() {
458
475
  }
459
476
  client.setAdvertisingId({});
460
477
  }
461
- function collectDeviceInfo(natives) {
478
+ function consentEquals(a, b) {
479
+ return a.isUserSubjectToGDPR === b.isUserSubjectToGDPR && a.hasConsentForDataUsage === b.hasConsentForDataUsage && a.hasConsentForAdsPersonalization === b.hasConsentForAdsPersonalization && a.hasConsentForAdStorage === b.hasConsentForAdStorage;
480
+ }
481
+ function resolveDmaConsent() {
482
+ if (manualConsentSet) return null;
483
+ const native = getIosNative() ?? getAndroidNative();
484
+ if (!native || typeof native.getTcfData !== "function") return null;
485
+ let consent = null;
486
+ try {
487
+ consent = (0, import_sdk_core.tcfToConsent)(native.getTcfData());
488
+ } catch (err) {
489
+ reportError("consent.tcfRead", err);
490
+ return null;
491
+ }
492
+ if (!consent) return null;
493
+ const client = active();
494
+ const current = client.getConsentData();
495
+ if (current && consentEquals(current, consent)) return null;
496
+ client.setConsentData(consent);
497
+ void resolveAdvertisingId().catch((err) => reportError("enrich.advertisingId", err));
498
+ return consent;
499
+ }
500
+ function readNativeDeviceInfo(natives) {
462
501
  try {
463
502
  if (natives.platform === "android" && natives.android?.getDeviceInfo) {
464
503
  return natives.android.getDeviceInfo();
@@ -470,6 +509,10 @@ function collectDeviceInfo(natives) {
470
509
  }
471
510
  return {};
472
511
  }
512
+ function collectDeviceInfo(natives) {
513
+ const { shortVersion: _shortVersion, ...info } = readNativeDeviceInfo(natives);
514
+ return info;
515
+ }
473
516
  function applyAppState(client, status) {
474
517
  if (status === "active") {
475
518
  client.notifyAppActive();
@@ -486,9 +529,12 @@ var INSTALL_SENT_KEY = "advenue.install_sent";
486
529
  var installInFlight = false;
487
530
  var autoInstallPending = false;
488
531
  var autoInstallEnabled = true;
489
- var configuredAppId = null;
532
+ var manualConsentSet = false;
533
+ var tcfCollectionEnabled = false;
534
+ var ATTEST_CHALLENGE_TIMEOUT_MS = 5e3;
490
535
  var configuredApiKey = null;
491
536
  var configuredEndpoint = import_sdk_core.DEFAULT_ENDPOINT;
537
+ var aemSeenUrlHashes = /* @__PURE__ */ new Set();
492
538
  function active() {
493
539
  if (!instance) {
494
540
  throw new Error("Advenue.initialize() must be called before tracking events.");
@@ -525,11 +571,19 @@ function fireAutoInstallIfPending() {
525
571
  }
526
572
  var Advenue = {
527
573
  async initialize(config) {
528
- const { mmkv, secureStore: secureMod, onDeepLink, appId, autoTrackInstall, ...rest } = config;
574
+ const {
575
+ mmkv,
576
+ secureStore: secureMod,
577
+ onDeepLink,
578
+ autoTrackInstall,
579
+ tcfDataCollection,
580
+ ...rest
581
+ } = config;
529
582
  autoInstallPending = false;
583
+ manualConsentSet = false;
584
+ tcfCollectionEnabled = tcfDataCollection === true;
530
585
  autoInstallEnabled = autoTrackInstall !== false;
531
586
  const platform = resolvePlatform(rest.platform);
532
- configuredAppId = appId ?? null;
533
587
  configuredApiKey = config.apiKey;
534
588
  configuredEndpoint = config.endpoint ?? import_sdk_core.DEFAULT_ENDPOINT;
535
589
  appStateSub?.remove();
@@ -544,20 +598,26 @@ var Advenue = {
544
598
  secureStore = secureMod ? createSecureStore(secureMod) : createNativeSecureStore(iosMod);
545
599
  const deviceId = rest.deviceId ?? await (0, import_sdk_core.resolveDurableDeviceId)(storage, secureStore ?? void 0);
546
600
  const osVersion = rest.osVersion ?? getOsVersion() ?? void 0;
601
+ const nativeDeviceInfo = readNativeDeviceInfo({
602
+ platform,
603
+ ios: getIosNative(),
604
+ android: getAndroidNative()
605
+ });
606
+ const { shortVersion, ...deviceInfo } = nativeDeviceInfo;
607
+ const appVersion = rest.appVersion ?? shortVersion ?? void 0;
547
608
  instance = new import_sdk_core.AdvenueClient({
548
609
  ...rest,
549
610
  platform,
550
611
  deviceId,
551
612
  storage,
552
- ...osVersion ? { osVersion } : {}
613
+ ...osVersion ? { osVersion } : {},
614
+ ...appVersion ? { appVersion } : {},
615
+ // Stamped by the SDK, never by the host app — see AdvenueConfig.sdkVersion.
616
+ sdkVersion: package_default.version
553
617
  });
554
618
  instance.debugLog(`SDK v${package_default.version} \u2014 events -> ${configuredEndpoint}`);
619
+ if (tcfCollectionEnabled) resolveDmaConsent();
555
620
  void resolveAdvertisingId().catch((err) => reportError("enrich.advertisingId", err));
556
- const deviceInfo = collectDeviceInfo({
557
- platform,
558
- ios: getIosNative(),
559
- android: getAndroidNative()
560
- });
561
621
  if (Object.keys(deviceInfo).length > 0) instance.setDeviceInfo(deviceInfo);
562
622
  if (!config.disableAutoSessions) {
563
623
  const appState = getAppState();
@@ -568,10 +628,12 @@ var Advenue = {
568
628
  if (!instance) return;
569
629
  if (status === "background") {
570
630
  seenBackground = true;
631
+ if (tcfCollectionEnabled) resolveDmaConsent();
571
632
  applyAppState(instance, status);
572
633
  } else if (status === "active") {
573
634
  if (!seenBackground) return;
574
635
  seenBackground = false;
636
+ if (tcfCollectionEnabled) resolveDmaConsent();
575
637
  applyAppState(instance, status);
576
638
  void resolveAdvertisingId().catch((err) => reportError("enrich.advertisingId", err));
577
639
  }
@@ -582,7 +644,7 @@ var Advenue = {
582
644
  instance.notifyAppActive();
583
645
  }
584
646
  }
585
- if (onDeepLink) {
647
+ {
586
648
  const linking = getLinking();
587
649
  if (linking) {
588
650
  const client = instance;
@@ -592,7 +654,19 @@ var Advenue = {
592
654
  // Deferred deep link: server-side conversion poll (deterministic ids
593
655
  // where available, probabilistic otherwise).
594
656
  resolveDeferred: () => client.resolveDeferredDeepLink(),
595
- onDeepLink
657
+ onDeepLink: onDeepLink ?? (() => {
658
+ }),
659
+ onDirectUrl: (url, link) => {
660
+ const raw = link.params?.al_applink_data;
661
+ if (typeof raw !== "string") return;
662
+ const campaignIds = extractAemCampaignIds(raw);
663
+ if (!campaignIds) return;
664
+ const sourceUrlHash = sha256Hex(url);
665
+ if (aemSeenUrlHashes.has(sourceUrlHash)) return;
666
+ if (client.getRequireConsent() && !client.getTrackingConsent()) return;
667
+ client.track("adv_meta_aem", { campaignIds, sourceUrlHash });
668
+ aemSeenUrlHashes.add(sourceUrlHash);
669
+ }
596
670
  });
597
671
  deepLinkSub.ready.catch((err) => reportError("deepLink.resolve", err));
598
672
  }
@@ -645,12 +719,18 @@ var Advenue = {
645
719
  }
646
720
  },
647
721
  setConsentData(consent) {
722
+ manualConsentSet = true;
648
723
  active().setConsentData(consent);
649
724
  void resolveAdvertisingId().catch((err) => reportError("enrich.advertisingId", err));
650
725
  },
651
726
  getConsentData: () => active().getConsentData(),
652
727
  flush: () => active().flush(),
653
728
  getDeviceId: () => active().getDeviceId(),
729
+ /** The Advenue ID — alias of getDeviceId (see AdvenueClient.getAdvenueId). */
730
+ getAdvenueId: () => active().getAdvenueId(),
731
+ /** Which app the server resolved this API key to; null until a batch lands
732
+ * (see AdvenueClient.getResolvedAppId). */
733
+ getResolvedAppId: () => active().getResolvedAppId(),
654
734
  shutdown() {
655
735
  appStateSub?.remove();
656
736
  appStateSub = null;
@@ -662,9 +742,11 @@ var Advenue = {
662
742
  installInFlight = false;
663
743
  autoInstallPending = false;
664
744
  autoInstallEnabled = true;
665
- configuredAppId = null;
745
+ manualConsentSet = false;
746
+ tcfCollectionEnabled = false;
666
747
  configuredApiKey = null;
667
748
  configuredEndpoint = import_sdk_core.DEFAULT_ENDPOINT;
749
+ aemSeenUrlHashes.clear();
668
750
  }
669
751
  };
670
752
  async function trackInstall() {
@@ -739,10 +821,10 @@ async function trackInstall() {
739
821
  }
740
822
  await resolveAdvertisingId();
741
823
  const trackOpts = { type: "install", ...opts };
742
- if (configuredAppId && android && typeof android.getIntegrityToken === "function") {
824
+ if (android && typeof android.getIntegrityToken === "function") {
743
825
  try {
744
826
  const deviceId = client.getDeviceId();
745
- const requestHash = sha256Hex(`${configuredAppId}:${deviceId}`);
827
+ const requestHash = sha256Hex(deviceId);
746
828
  const token = await android.getIntegrityToken(requestHash);
747
829
  if (token) {
748
830
  trackOpts.attestationToken = token;
@@ -752,13 +834,24 @@ async function trackInstall() {
752
834
  reportError("attest.playIntegrity", err);
753
835
  }
754
836
  }
755
- if (configuredAppId && configuredApiKey && ios && typeof ios.attestKey === "function") {
837
+ if (configuredApiKey && ios && typeof ios.attestKey === "function") {
756
838
  try {
757
839
  const deviceId = client.getDeviceId();
758
840
  const challengeUrl = `${configuredEndpoint}/v1/attest/challenge?deviceId=${encodeURIComponent(deviceId)}`;
759
- const challengeRes = await fetch(challengeUrl, {
760
- headers: { "x-api-key": configuredApiKey }
761
- });
841
+ const ac = new AbortController();
842
+ const timer = setTimeout(
843
+ () => ac.abort(new Error("attest challenge timed out")),
844
+ ATTEST_CHALLENGE_TIMEOUT_MS
845
+ );
846
+ let challengeRes;
847
+ try {
848
+ challengeRes = await fetch(challengeUrl, {
849
+ headers: { "x-api-key": configuredApiKey },
850
+ signal: ac.signal
851
+ });
852
+ } finally {
853
+ clearTimeout(timer);
854
+ }
762
855
  if (!challengeRes.ok) throw new Error(`challenge HTTP ${challengeRes.status}`);
763
856
  const { challenge } = await challengeRes.json();
764
857
  const { keyId, attestationObject } = await ios.attestKey(challenge);
@@ -815,5 +908,6 @@ async function trackInstall() {
815
908
  getIosNative,
816
909
  parseDirectLink,
817
910
  resolveAdvertisingId,
911
+ resolveDmaConsent,
818
912
  trackInstall
819
913
  });
package/dist/index.d.cts CHANGED
@@ -44,6 +44,14 @@ interface MetaInstallReferrerResult {
44
44
  * real `DeviceInfo` shape.
45
45
  */
46
46
  interface AdvenueDeviceInfo {
47
+ /** Bundle id / package name — Meta CAPI extinfo slot 1. */
48
+ packageName?: string;
49
+ /** User-facing app version (CFBundleShortVersionString / versionName) —
50
+ * extinfo slot 2. Surfaced as the event's top-level `appVersion`, not as
51
+ * part of the deviceInfo blob. */
52
+ shortVersion?: string;
53
+ /** Build identifier (CFBundleVersion / versionCode) — extinfo slot 3. */
54
+ longVersion?: string;
47
55
  model?: string;
48
56
  locale?: string;
49
57
  carrier?: string;
@@ -55,6 +63,16 @@ interface AdvenueDeviceInfo {
55
63
  totalStorageGb?: number;
56
64
  freeStorageGb?: number;
57
65
  }
66
+ /**
67
+ * Raw IAB TCF v2 CMP values from the platform default store
68
+ * (`UserDefaults.standard` / default SharedPreferences) — where TCF-compliant
69
+ * CMPs (e.g. Google UMP) write `IABTCF_*` keys. Structurally mirrors
70
+ * `TcfData` from `@advenue/sdk-core`.
71
+ */
72
+ interface NativeTcfData {
73
+ gdprApplies?: number | null;
74
+ purposeConsents?: string | null;
75
+ }
58
76
  interface AdvenueAndroidNativeModule {
59
77
  /** Raw Play Install Referrer; parse with @advenue/install-referrer. */
60
78
  getInstallReferrer(): Promise<InstallReferrerResult | null>;
@@ -73,9 +91,11 @@ interface AdvenueAndroidNativeModule {
73
91
  /**
74
92
  * Play Integrity Standard API token (G3.1n).
75
93
  *
76
- * `requestHash` MUST be sha256_hex("<appId>:<deviceId>") — lowercase hex SHA-256
77
- * of the literal string "<appId>:<deviceId>". The server's mapIntegrityVerdict
78
- * re-derives the same string and compares it byte-for-byte.
94
+ * `requestHash` MUST be sha256_hex("<deviceId>") — lowercase hex SHA-256 of the
95
+ * device id alone. The server's mapIntegrityVerdict re-derives the same string
96
+ * and compares it byte-for-byte. No app id is hashed in: the token is decoded
97
+ * against the app's Play package name and must be PLAY_RECOGNIZED, which binds
98
+ * the app already.
79
99
  *
80
100
  * Rejects when:
81
101
  * - ECONFIG: `io.advenue.INTEGRITY_CLOUD_PROJECT_NUMBER` meta-data is absent.
@@ -112,6 +132,12 @@ interface AdvenueAndroidNativeModule {
112
132
  * policy). Optional: absent on older native builds; feature-detected.
113
133
  */
114
134
  getAndroidId?(): string | null;
135
+ /**
136
+ * IAB TCF v2 CMP data, null when no CMP has written TCF keys. Optional:
137
+ * older native builds lack it — callers feature-detect (getAndroidId
138
+ * precedent).
139
+ */
140
+ getTcfData?(): NativeTcfData | null;
115
141
  }
116
142
  type TrackingAuthorizationStatus = 'notDetermined' | 'restricted' | 'denied' | 'authorized';
117
143
  interface AdvenueIosNativeModule {
@@ -176,6 +202,12 @@ interface AdvenueIosNativeModule {
176
202
  secureGetItem?(key: string): string | null;
177
203
  secureSetItem?(key: string, value: string): void;
178
204
  secureRemoveItem?(key: string): void;
205
+ /**
206
+ * IAB TCF v2 CMP data, null when no CMP has written TCF keys. Optional:
207
+ * older native builds lack it — callers feature-detect (getAndroidId
208
+ * precedent).
209
+ */
210
+ getTcfData?(): NativeTcfData | null;
179
211
  }
180
212
 
181
213
  /**
@@ -219,6 +251,20 @@ declare function getAndroidNative(): AdvenueAndroidNativeModule | null;
219
251
  * Fire-and-forget at call sites; errors are suppressed internally.
220
252
  */
221
253
  declare function resolveAdvertisingId(): Promise<void>;
254
+ /**
255
+ * Reads IAB TCF v2 consent (written by a TCF-compliant CMP, e.g. Google UMP,
256
+ * to the platform default store), maps it to the DMA Consent shape, and
257
+ * pushes it into the active client. No-op — returning null — when consent was
258
+ * set manually this session, the native module or its getter is absent, no
259
+ * TCF data exists, or the freshly-mapped consent is identical to what the
260
+ * client already has (skip-if-unchanged: avoids re-persisting to storage and
261
+ * re-firing the ad-id resolve on every foreground when nothing changed):
262
+ * absent/unchanged consent must stay untouched, never fabricated or re-churned.
263
+ *
264
+ * Must be called after `Advenue.initialize()` — like every facade method, it
265
+ * throws via the uninitialized guard otherwise.
266
+ */
267
+ declare function resolveDmaConsent(): Consent | null;
222
268
  /**
223
269
  * Collects native device metadata (model, locale, screen, cpu, storage, timezone)
224
270
  * used to populate Meta CAPI `extinfo`. Reads from the platform-appropriate
@@ -228,15 +274,19 @@ declare function resolveAdvertisingId(): Promise<void>;
228
274
  * native-side error degrades to an empty object so callers can unconditionally
229
275
  * pass the result through.
230
276
  */
231
- declare function collectDeviceInfo(natives: {
277
+ type NativeDeviceInfo = Partial<DeviceInfo> & {
278
+ shortVersion?: string;
279
+ };
280
+ type DeviceInfoNatives = {
232
281
  platform: 'ios' | 'android' | string;
233
282
  ios?: {
234
- getDeviceInfo?: () => Partial<DeviceInfo>;
283
+ getDeviceInfo?: () => NativeDeviceInfo;
235
284
  } | null;
236
285
  android?: {
237
- getDeviceInfo?: () => Partial<DeviceInfo>;
286
+ getDeviceInfo?: () => NativeDeviceInfo;
238
287
  } | null;
239
- }): DeviceInfo;
288
+ };
289
+ declare function collectDeviceInfo(natives: DeviceInfoNatives): DeviceInfo;
240
290
  interface RNAdvenueConfig extends Omit<AdvenueConfig, 'storage' | 'platform'> {
241
291
  /**
242
292
  * Auto-detected from React Native's `Platform.OS` — omit it. Pass explicitly
@@ -251,18 +301,6 @@ interface RNAdvenueConfig extends Omit<AdvenueConfig, 'storage' | 'platform'> {
251
301
  * supply storage. When set, it replaces the built-in native storage.
252
302
  */
253
303
  mmkv?: MMKVLike;
254
- /**
255
- * G3.1n — Play Integrity (Android) / App Attest (iOS) attestation.
256
- *
257
- * Your app's Advenue appId (the UUID in Settings → API Keys). When set on
258
- * Android, the automatic install obtains a Play Integrity Standard token and
259
- * attaches it to the install event. The requestHash is computed as
260
- * `sha256Hex("<appId>:<deviceId>")`, matching the server's verification
261
- * formula exactly (see `makeDecodeAndroid` in the worker).
262
- *
263
- * Omit to run without attestation (the server field stays `unavailable`).
264
- */
265
- appId?: string;
266
304
  /**
267
305
  * @internal Secure-store override (tests / custom embeddings). The SDK uses
268
306
  * its own Keychain-backed native storage on iOS for the reinstall-resilient
@@ -285,6 +323,14 @@ interface RNAdvenueConfig extends Omit<AdvenueConfig, 'storage' | 'platform'> {
285
323
  * install path deterministically.
286
324
  */
287
325
  autoTrackInstall?: boolean;
326
+ /**
327
+ * Auto-collect IAB TCF v2 consent written by a TCF-compliant CMP (e.g.
328
+ * Google UMP) and attach it to every event as DMA consent data (AppsFlyer
329
+ * `enableTCFDataCollection` parity). Read at init and re-read on each
330
+ * foreground, so a CMP decision made after startup lands on later events.
331
+ * A manual `setConsentData()` call always wins. Default false.
332
+ */
333
+ tcfDataCollection?: boolean;
288
334
  }
289
335
  /**
290
336
  * Maps a React Native AppState status to the client's session lifecycle hooks.
@@ -317,6 +363,11 @@ declare const Advenue: {
317
363
  } | null;
318
364
  flush: () => Promise<void>;
319
365
  getDeviceId: () => string;
366
+ /** The Advenue ID — alias of getDeviceId (see AdvenueClient.getAdvenueId). */
367
+ getAdvenueId: () => string;
368
+ /** Which app the server resolved this API key to; null until a batch lands
369
+ * (see AdvenueClient.getResolvedAppId). */
370
+ getResolvedAppId: () => string | null;
320
371
  shutdown(): void;
321
372
  };
322
373
  /**
@@ -333,4 +384,4 @@ declare const Advenue: {
333
384
  */
334
385
  declare function trackInstall(): Promise<void>;
335
386
 
336
- export { Advenue, type AdvenueAndroidNativeModule, type AdvenueDeviceInfo, type AdvenueIosNativeModule, type InstallReferrerResult, type MMKVLike, type MetaInstallReferrerResult, type RNAdvenueConfig, type SecureStoreLike, type TrackingAuthorizationStatus, applyAppState, collectDeviceInfo, createMMKVStorage, createSecureStore, getAndroidNative, getIosNative, parseDirectLink, resolveAdvertisingId, trackInstall };
387
+ export { Advenue, type AdvenueAndroidNativeModule, type AdvenueDeviceInfo, type AdvenueIosNativeModule, type InstallReferrerResult, type MMKVLike, type MetaInstallReferrerResult, type NativeTcfData, type RNAdvenueConfig, type SecureStoreLike, type TrackingAuthorizationStatus, applyAppState, collectDeviceInfo, createMMKVStorage, createSecureStore, getAndroidNative, getIosNative, parseDirectLink, resolveAdvertisingId, resolveDmaConsent, trackInstall };