@advenue/react-native 0.6.5 → 0.7.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/dist/index.cjs +25 -33
- package/dist/index.d.cts +1 -11
- package/dist/index.d.ts +1 -11
- package/dist/index.js +25 -32
- package/ios/AdvenueIos.podspec +4 -0
- package/ios/AdvenueIosModule.swift +0 -11
- package/ios/PrivacyInfo.xcprivacy +56 -0
- package/package.json +2 -2
- package/src/index.ts +14 -37
- package/src/native-types.ts +0 -2
- package/src/native.ts +23 -0
package/dist/index.cjs
CHANGED
|
@@ -63,21 +63,12 @@ __export(index_exports, {
|
|
|
63
63
|
getIosNative: () => getIosNative,
|
|
64
64
|
parseDirectLink: () => parseDirectLink,
|
|
65
65
|
resolveAdvertisingId: () => resolveAdvertisingId,
|
|
66
|
-
resolveClipboardMatch: () => resolveClipboardMatch,
|
|
67
66
|
trackInstall: () => trackInstall
|
|
68
67
|
});
|
|
69
68
|
module.exports = __toCommonJS(index_exports);
|
|
70
69
|
var import_install_referrer = require("@advenue/install-referrer");
|
|
71
70
|
var import_sdk_core = require("@advenue/sdk-core");
|
|
72
71
|
|
|
73
|
-
// ../shared/src/match-token.ts
|
|
74
|
-
var MATCH_TOKEN_PREFIX = "advmatch:";
|
|
75
|
-
function parseMatchToken(raw) {
|
|
76
|
-
if (!raw || !raw.startsWith(MATCH_TOKEN_PREFIX)) return null;
|
|
77
|
-
const id = raw.slice(MATCH_TOKEN_PREFIX.length).trim();
|
|
78
|
-
return /^[A-Za-z0-9_-]{1,128}$/.test(id) ? id : null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
72
|
// ../signing/src/index.ts
|
|
82
73
|
var K = new Uint32Array([
|
|
83
74
|
1116352408,
|
|
@@ -230,7 +221,7 @@ function sha256Hex(message) {
|
|
|
230
221
|
// package.json
|
|
231
222
|
var package_default = {
|
|
232
223
|
name: "@advenue/react-native",
|
|
233
|
-
version: "0.
|
|
224
|
+
version: "0.7.0",
|
|
234
225
|
description: "Advenue MMP React Native SDK \u2014 attribution, install tracking, and deep links (bundled iOS/Android Expo native modules)",
|
|
235
226
|
type: "module",
|
|
236
227
|
license: "MIT",
|
|
@@ -366,6 +357,19 @@ function getPlatformOS() {
|
|
|
366
357
|
return null;
|
|
367
358
|
}
|
|
368
359
|
}
|
|
360
|
+
function getOsVersion() {
|
|
361
|
+
try {
|
|
362
|
+
const rn = (0, import_native_require.requireReactNative)();
|
|
363
|
+
if (!rn) return null;
|
|
364
|
+
if (rn.Platform.OS === "android") {
|
|
365
|
+
const release = rn.Platform.constants?.Release;
|
|
366
|
+
return release != null ? String(release) : null;
|
|
367
|
+
}
|
|
368
|
+
return rn.Platform.Version != null ? String(rn.Platform.Version) : null;
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
369
373
|
function getAppState() {
|
|
370
374
|
try {
|
|
371
375
|
const rn = (0, import_native_require.requireReactNative)();
|
|
@@ -466,24 +470,6 @@ function collectDeviceInfo(natives) {
|
|
|
466
470
|
}
|
|
467
471
|
return {};
|
|
468
472
|
}
|
|
469
|
-
async function resolveClipboardMatch() {
|
|
470
|
-
const client = active();
|
|
471
|
-
if (client.getRequireConsent() && !client.getTrackingConsent()) return null;
|
|
472
|
-
const ios = getIosNative();
|
|
473
|
-
if (!ios || typeof ios.getPasteboardMatchToken !== "function") {
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
let token;
|
|
477
|
-
try {
|
|
478
|
-
token = ios.getPasteboardMatchToken();
|
|
479
|
-
} catch (err) {
|
|
480
|
-
reportError("enrich.clipboardMatch", err);
|
|
481
|
-
return null;
|
|
482
|
-
}
|
|
483
|
-
const matchId = parseMatchToken(token);
|
|
484
|
-
if (!matchId) return null;
|
|
485
|
-
return client.exchangeClipboardMatch(matchId);
|
|
486
|
-
}
|
|
487
473
|
function applyAppState(client, status) {
|
|
488
474
|
if (status === "active") {
|
|
489
475
|
client.notifyAppActive();
|
|
@@ -557,7 +543,14 @@ var Advenue = {
|
|
|
557
543
|
installFlagStore = storage;
|
|
558
544
|
secureStore = secureMod ? createSecureStore(secureMod) : createNativeSecureStore(iosMod);
|
|
559
545
|
const deviceId = rest.deviceId ?? await (0, import_sdk_core.resolveDurableDeviceId)(storage, secureStore ?? void 0);
|
|
560
|
-
|
|
546
|
+
const osVersion = rest.osVersion ?? getOsVersion() ?? void 0;
|
|
547
|
+
instance = new import_sdk_core.AdvenueClient({
|
|
548
|
+
...rest,
|
|
549
|
+
platform,
|
|
550
|
+
deviceId,
|
|
551
|
+
storage,
|
|
552
|
+
...osVersion ? { osVersion } : {}
|
|
553
|
+
});
|
|
561
554
|
instance.debugLog(`SDK v${package_default.version} \u2014 events -> ${configuredEndpoint}`);
|
|
562
555
|
void resolveAdvertisingId().catch((err) => reportError("enrich.advertisingId", err));
|
|
563
556
|
const deviceInfo = collectDeviceInfo({
|
|
@@ -596,9 +589,9 @@ var Advenue = {
|
|
|
596
589
|
deepLinkSub = setupDeepLinks({
|
|
597
590
|
getInitialURL: () => linking.getInitialURL(),
|
|
598
591
|
addUrlListener: (handler) => linking.addEventListener("url", (e) => handler(e.url)),
|
|
599
|
-
// Deferred
|
|
600
|
-
//
|
|
601
|
-
resolveDeferred:
|
|
592
|
+
// Deferred deep link: server-side conversion poll (deterministic ids
|
|
593
|
+
// where available, probabilistic otherwise).
|
|
594
|
+
resolveDeferred: () => client.resolveDeferredDeepLink(),
|
|
602
595
|
onDeepLink
|
|
603
596
|
});
|
|
604
597
|
deepLinkSub.ready.catch((err) => reportError("deepLink.resolve", err));
|
|
@@ -822,6 +815,5 @@ async function trackInstall() {
|
|
|
822
815
|
getIosNative,
|
|
823
816
|
parseDirectLink,
|
|
824
817
|
resolveAdvertisingId,
|
|
825
|
-
resolveClipboardMatch,
|
|
826
818
|
trackInstall
|
|
827
819
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -132,8 +132,6 @@ interface AdvenueIosNativeModule {
|
|
|
132
132
|
getAdvertisingId(): Promise<string | null>;
|
|
133
133
|
/** Vendor-scoped device id (IDFV) — available without ATT; a fallback anchor. */
|
|
134
134
|
getIdentifierForVendor(): string | null;
|
|
135
|
-
/** Clipboard match token (advmatch:<id>) for deterministic deferred deep linking; null if absent. */
|
|
136
|
-
getPasteboardMatchToken(): string | null;
|
|
137
135
|
/**
|
|
138
136
|
* G3.2n — App Attest. Generates (or reuses a keychain-cached) DCAppAttest
|
|
139
137
|
* key, computes clientDataHash = SHA-256(challenge), and calls
|
|
@@ -239,14 +237,6 @@ declare function collectDeviceInfo(natives: {
|
|
|
239
237
|
getDeviceInfo?: () => Partial<DeviceInfo>;
|
|
240
238
|
} | null;
|
|
241
239
|
}): DeviceInfo;
|
|
242
|
-
/**
|
|
243
|
-
* iOS clipboard-match deferred deep linking: reads the `advmatch:<id>` token the
|
|
244
|
-
* link redirector wrote to the pasteboard and exchanges it for the deep link — a
|
|
245
|
-
* DETERMINISTIC match (no IDFA, no fingerprinting). Returns null off iOS, when
|
|
246
|
-
* there's no token, or without consent. (Reading the pasteboard shows the iOS
|
|
247
|
-
* paste banner — the user-visible, privacy-forward deferred path.)
|
|
248
|
-
*/
|
|
249
|
-
declare function resolveClipboardMatch(): Promise<DeepLink | null>;
|
|
250
240
|
interface RNAdvenueConfig extends Omit<AdvenueConfig, 'storage' | 'platform'> {
|
|
251
241
|
/**
|
|
252
242
|
* Auto-detected from React Native's `Platform.OS` — omit it. Pass explicitly
|
|
@@ -343,4 +333,4 @@ declare const Advenue: {
|
|
|
343
333
|
*/
|
|
344
334
|
declare function trackInstall(): Promise<void>;
|
|
345
335
|
|
|
346
|
-
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,
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -132,8 +132,6 @@ interface AdvenueIosNativeModule {
|
|
|
132
132
|
getAdvertisingId(): Promise<string | null>;
|
|
133
133
|
/** Vendor-scoped device id (IDFV) — available without ATT; a fallback anchor. */
|
|
134
134
|
getIdentifierForVendor(): string | null;
|
|
135
|
-
/** Clipboard match token (advmatch:<id>) for deterministic deferred deep linking; null if absent. */
|
|
136
|
-
getPasteboardMatchToken(): string | null;
|
|
137
135
|
/**
|
|
138
136
|
* G3.2n — App Attest. Generates (or reuses a keychain-cached) DCAppAttest
|
|
139
137
|
* key, computes clientDataHash = SHA-256(challenge), and calls
|
|
@@ -239,14 +237,6 @@ declare function collectDeviceInfo(natives: {
|
|
|
239
237
|
getDeviceInfo?: () => Partial<DeviceInfo>;
|
|
240
238
|
} | null;
|
|
241
239
|
}): DeviceInfo;
|
|
242
|
-
/**
|
|
243
|
-
* iOS clipboard-match deferred deep linking: reads the `advmatch:<id>` token the
|
|
244
|
-
* link redirector wrote to the pasteboard and exchanges it for the deep link — a
|
|
245
|
-
* DETERMINISTIC match (no IDFA, no fingerprinting). Returns null off iOS, when
|
|
246
|
-
* there's no token, or without consent. (Reading the pasteboard shows the iOS
|
|
247
|
-
* paste banner — the user-visible, privacy-forward deferred path.)
|
|
248
|
-
*/
|
|
249
|
-
declare function resolveClipboardMatch(): Promise<DeepLink | null>;
|
|
250
240
|
interface RNAdvenueConfig extends Omit<AdvenueConfig, 'storage' | 'platform'> {
|
|
251
241
|
/**
|
|
252
242
|
* Auto-detected from React Native's `Platform.OS` — omit it. Pass explicitly
|
|
@@ -343,4 +333,4 @@ declare const Advenue: {
|
|
|
343
333
|
*/
|
|
344
334
|
declare function trackInstall(): Promise<void>;
|
|
345
335
|
|
|
346
|
-
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,
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -65,14 +65,6 @@ import {
|
|
|
65
65
|
resolveDurableDeviceId
|
|
66
66
|
} from "@advenue/sdk-core";
|
|
67
67
|
|
|
68
|
-
// ../shared/src/match-token.ts
|
|
69
|
-
var MATCH_TOKEN_PREFIX = "advmatch:";
|
|
70
|
-
function parseMatchToken(raw) {
|
|
71
|
-
if (!raw || !raw.startsWith(MATCH_TOKEN_PREFIX)) return null;
|
|
72
|
-
const id = raw.slice(MATCH_TOKEN_PREFIX.length).trim();
|
|
73
|
-
return /^[A-Za-z0-9_-]{1,128}$/.test(id) ? id : null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
68
|
// ../signing/src/index.ts
|
|
77
69
|
var K = new Uint32Array([
|
|
78
70
|
1116352408,
|
|
@@ -225,7 +217,7 @@ function sha256Hex(message) {
|
|
|
225
217
|
// package.json
|
|
226
218
|
var package_default = {
|
|
227
219
|
name: "@advenue/react-native",
|
|
228
|
-
version: "0.
|
|
220
|
+
version: "0.7.0",
|
|
229
221
|
description: "Advenue MMP React Native SDK \u2014 attribution, install tracking, and deep links (bundled iOS/Android Expo native modules)",
|
|
230
222
|
type: "module",
|
|
231
223
|
license: "MIT",
|
|
@@ -361,6 +353,19 @@ function getPlatformOS() {
|
|
|
361
353
|
return null;
|
|
362
354
|
}
|
|
363
355
|
}
|
|
356
|
+
function getOsVersion() {
|
|
357
|
+
try {
|
|
358
|
+
const rn = (0, import_native_require.requireReactNative)();
|
|
359
|
+
if (!rn) return null;
|
|
360
|
+
if (rn.Platform.OS === "android") {
|
|
361
|
+
const release = rn.Platform.constants?.Release;
|
|
362
|
+
return release != null ? String(release) : null;
|
|
363
|
+
}
|
|
364
|
+
return rn.Platform.Version != null ? String(rn.Platform.Version) : null;
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
364
369
|
function getAppState() {
|
|
365
370
|
try {
|
|
366
371
|
const rn = (0, import_native_require.requireReactNative)();
|
|
@@ -461,24 +466,6 @@ function collectDeviceInfo(natives) {
|
|
|
461
466
|
}
|
|
462
467
|
return {};
|
|
463
468
|
}
|
|
464
|
-
async function resolveClipboardMatch() {
|
|
465
|
-
const client = active();
|
|
466
|
-
if (client.getRequireConsent() && !client.getTrackingConsent()) return null;
|
|
467
|
-
const ios = getIosNative();
|
|
468
|
-
if (!ios || typeof ios.getPasteboardMatchToken !== "function") {
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
471
|
-
let token;
|
|
472
|
-
try {
|
|
473
|
-
token = ios.getPasteboardMatchToken();
|
|
474
|
-
} catch (err) {
|
|
475
|
-
reportError("enrich.clipboardMatch", err);
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
const matchId = parseMatchToken(token);
|
|
479
|
-
if (!matchId) return null;
|
|
480
|
-
return client.exchangeClipboardMatch(matchId);
|
|
481
|
-
}
|
|
482
469
|
function applyAppState(client, status) {
|
|
483
470
|
if (status === "active") {
|
|
484
471
|
client.notifyAppActive();
|
|
@@ -552,7 +539,14 @@ var Advenue = {
|
|
|
552
539
|
installFlagStore = storage;
|
|
553
540
|
secureStore = secureMod ? createSecureStore(secureMod) : createNativeSecureStore(iosMod);
|
|
554
541
|
const deviceId = rest.deviceId ?? await resolveDurableDeviceId(storage, secureStore ?? void 0);
|
|
555
|
-
|
|
542
|
+
const osVersion = rest.osVersion ?? getOsVersion() ?? void 0;
|
|
543
|
+
instance = new AdvenueClient({
|
|
544
|
+
...rest,
|
|
545
|
+
platform,
|
|
546
|
+
deviceId,
|
|
547
|
+
storage,
|
|
548
|
+
...osVersion ? { osVersion } : {}
|
|
549
|
+
});
|
|
556
550
|
instance.debugLog(`SDK v${package_default.version} \u2014 events -> ${configuredEndpoint}`);
|
|
557
551
|
void resolveAdvertisingId().catch((err) => reportError("enrich.advertisingId", err));
|
|
558
552
|
const deviceInfo = collectDeviceInfo({
|
|
@@ -591,9 +585,9 @@ var Advenue = {
|
|
|
591
585
|
deepLinkSub = setupDeepLinks({
|
|
592
586
|
getInitialURL: () => linking.getInitialURL(),
|
|
593
587
|
addUrlListener: (handler) => linking.addEventListener("url", (e) => handler(e.url)),
|
|
594
|
-
// Deferred
|
|
595
|
-
//
|
|
596
|
-
resolveDeferred:
|
|
588
|
+
// Deferred deep link: server-side conversion poll (deterministic ids
|
|
589
|
+
// where available, probabilistic otherwise).
|
|
590
|
+
resolveDeferred: () => client.resolveDeferredDeepLink(),
|
|
597
591
|
onDeepLink
|
|
598
592
|
});
|
|
599
593
|
deepLinkSub.ready.catch((err) => reportError("deepLink.resolve", err));
|
|
@@ -816,6 +810,5 @@ export {
|
|
|
816
810
|
getIosNative,
|
|
817
811
|
parseDirectLink,
|
|
818
812
|
resolveAdvertisingId,
|
|
819
|
-
resolveClipboardMatch,
|
|
820
813
|
trackInstall
|
|
821
814
|
};
|
package/ios/AdvenueIos.podspec
CHANGED
|
@@ -17,4 +17,8 @@ Pod::Spec.new do |s|
|
|
|
17
17
|
s.dependency 'ExpoModulesCore'
|
|
18
18
|
|
|
19
19
|
s.source_files = '**/*.{h,m,swift}'
|
|
20
|
+
# Apple privacy manifest (required since May 2024 for SDKs that persist
|
|
21
|
+
# state / collect device info) — shipped as a resource bundle so Xcode
|
|
22
|
+
# aggregates it into the app's privacy report.
|
|
23
|
+
s.resource_bundles = { 'AdvenueIos_Privacy' => ['PrivacyInfo.xcprivacy'] }
|
|
20
24
|
end
|
|
@@ -137,17 +137,6 @@ public class AdvenueIosModule: Module {
|
|
|
137
137
|
return Self.onMainSync { UIDevice.current.identifierForVendor?.uuidString }
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
// Reads our clipboard match token (advmatch:<id>) written by the link
|
|
141
|
-
// redirector, for deterministic deferred deep linking. Returns ONLY our
|
|
142
|
-
// token, never arbitrary clipboard contents. (Reading the pasteboard shows
|
|
143
|
-
// the iOS paste notification — expected, user-visible, deterministic path.)
|
|
144
|
-
Function("getPasteboardMatchToken") { () -> String? in
|
|
145
|
-
return Self.onMainSync { () -> String? in
|
|
146
|
-
guard let s = UIPasteboard.general.string, s.hasPrefix("advmatch:") else { return nil }
|
|
147
|
-
return s
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
140
|
// G3.3 — DeviceCheck token. Calls DCDevice.current.generateToken to obtain
|
|
152
141
|
// the opaque base64 token that identifies this physical device to the Apple
|
|
153
142
|
// DeviceCheck API. The server uses it (with the per-app DeviceCheck key) to
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
<!-- The SDK itself does not track (no fingerprinting; IDFA collection is
|
|
6
|
+
app-mediated via the ATT prompt) and calls no tracking domains. -->
|
|
7
|
+
<key>NSPrivacyTracking</key>
|
|
8
|
+
<false/>
|
|
9
|
+
<key>NSPrivacyTrackingDomains</key>
|
|
10
|
+
<array/>
|
|
11
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
12
|
+
<array>
|
|
13
|
+
<!-- Durable SDK device id (Keychain-backed) — attribution/analytics. -->
|
|
14
|
+
<dict>
|
|
15
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
16
|
+
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
|
17
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
18
|
+
<true/>
|
|
19
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
20
|
+
<false/>
|
|
21
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
22
|
+
<array>
|
|
23
|
+
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
|
24
|
+
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
25
|
+
</array>
|
|
26
|
+
</dict>
|
|
27
|
+
<!-- Coarse device characteristics (model, OS version, locale, screen) —
|
|
28
|
+
event enrichment / attribution corroboration. -->
|
|
29
|
+
<dict>
|
|
30
|
+
<key>NSPrivacyCollectedDataType</key>
|
|
31
|
+
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
|
|
32
|
+
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
33
|
+
<true/>
|
|
34
|
+
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
35
|
+
<false/>
|
|
36
|
+
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
37
|
+
<array>
|
|
38
|
+
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
39
|
+
</array>
|
|
40
|
+
</dict>
|
|
41
|
+
</array>
|
|
42
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
43
|
+
<array>
|
|
44
|
+
<!-- CA92.1: UserDefaults is read/written only for this app's own SDK
|
|
45
|
+
state (device id mirror, install flag, consent). -->
|
|
46
|
+
<dict>
|
|
47
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
48
|
+
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
49
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
50
|
+
<array>
|
|
51
|
+
<string>CA92.1</string>
|
|
52
|
+
</array>
|
|
53
|
+
</dict>
|
|
54
|
+
</array>
|
|
55
|
+
</dict>
|
|
56
|
+
</plist>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@advenue/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Advenue MMP React Native SDK — attribution, install tracking, and deep links (bundled iOS/Android Expo native modules)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@advenue/install-referrer": "0.
|
|
32
|
+
"@advenue/install-referrer": "0.3.0",
|
|
33
33
|
"@advenue/sdk-core": "0.4.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
type TrackOptions,
|
|
18
18
|
resolveDurableDeviceId,
|
|
19
19
|
} from '@advenue/sdk-core';
|
|
20
|
-
import { parseMatchToken } from '@advenue/shared/match-token';
|
|
21
20
|
import { sha256Hex } from '@advenue/signing';
|
|
22
21
|
import pkg from '../package.json' with { type: 'json' };
|
|
23
22
|
import { type DeepLinkHandle, setupDeepLinks } from './deep-links';
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
getAppState,
|
|
29
28
|
getIosNative,
|
|
30
29
|
getLinking,
|
|
30
|
+
getOsVersion,
|
|
31
31
|
getPlatformOS,
|
|
32
32
|
} from './native';
|
|
33
33
|
import { createNativeSecureStore, createNativeStorage } from './native-storage';
|
|
@@ -149,37 +149,6 @@ export function collectDeviceInfo(natives: {
|
|
|
149
149
|
return {};
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
/**
|
|
153
|
-
* iOS clipboard-match deferred deep linking: reads the `advmatch:<id>` token the
|
|
154
|
-
* link redirector wrote to the pasteboard and exchanges it for the deep link — a
|
|
155
|
-
* DETERMINISTIC match (no IDFA, no fingerprinting). Returns null off iOS, when
|
|
156
|
-
* there's no token, or without consent. (Reading the pasteboard shows the iOS
|
|
157
|
-
* paste banner — the user-visible, privacy-forward deferred path.)
|
|
158
|
-
*/
|
|
159
|
-
export async function resolveClipboardMatch(): Promise<DeepLink | null> {
|
|
160
|
-
const client = active();
|
|
161
|
-
if (client.getRequireConsent() && !client.getTrackingConsent()) return null;
|
|
162
|
-
const ios = getIosNative();
|
|
163
|
-
if (
|
|
164
|
-
!ios ||
|
|
165
|
-
typeof (ios as { getPasteboardMatchToken?: unknown }).getPasteboardMatchToken !== 'function'
|
|
166
|
-
) {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
let token: string | null;
|
|
170
|
-
try {
|
|
171
|
-
token = ios.getPasteboardMatchToken();
|
|
172
|
-
} catch (err) {
|
|
173
|
-
// Native pasteboard read failed (bridge error, MDM restriction) — deferred
|
|
174
|
-
// resolution falls through to the server-side conversion poll.
|
|
175
|
-
reportError('enrich.clipboardMatch', err);
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
const matchId = parseMatchToken(token);
|
|
179
|
-
if (!matchId) return null;
|
|
180
|
-
return client.exchangeClipboardMatch(matchId);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
152
|
export interface RNAdvenueConfig extends Omit<AdvenueConfig, 'storage' | 'platform'> {
|
|
184
153
|
/**
|
|
185
154
|
* Auto-detected from React Native's `Platform.OS` — omit it. Pass explicitly
|
|
@@ -371,7 +340,16 @@ export const Advenue = {
|
|
|
371
340
|
secureStore = secureMod ? createSecureStore(secureMod) : createNativeSecureStore(iosMod);
|
|
372
341
|
const deviceId =
|
|
373
342
|
rest.deviceId ?? (await resolveDurableDeviceId(storage, secureStore ?? undefined));
|
|
374
|
-
|
|
343
|
+
// Auto-collect the OS release version — it feeds the server's normalized
|
|
344
|
+
// probabilistic fingerprint (spec D1); an explicit config value wins.
|
|
345
|
+
const osVersion = rest.osVersion ?? getOsVersion() ?? undefined;
|
|
346
|
+
instance = new AdvenueClient({
|
|
347
|
+
...rest,
|
|
348
|
+
platform,
|
|
349
|
+
deviceId,
|
|
350
|
+
storage,
|
|
351
|
+
...(osVersion ? { osVersion } : {}),
|
|
352
|
+
});
|
|
375
353
|
// One-line ground truth for "which SDK build talks to which host" — the
|
|
376
354
|
// exact questions a silent integration always raises first.
|
|
377
355
|
instance.debugLog(`SDK v${pkg.version} — events -> ${configuredEndpoint}`);
|
|
@@ -421,10 +399,9 @@ export const Advenue = {
|
|
|
421
399
|
deepLinkSub = setupDeepLinks({
|
|
422
400
|
getInitialURL: () => linking.getInitialURL(),
|
|
423
401
|
addUrlListener: (handler) => linking.addEventListener('url', (e) => handler(e.url)),
|
|
424
|
-
// Deferred
|
|
425
|
-
//
|
|
426
|
-
resolveDeferred:
|
|
427
|
-
(await resolveClipboardMatch()) ?? client.resolveDeferredDeepLink(),
|
|
402
|
+
// Deferred deep link: server-side conversion poll (deterministic ids
|
|
403
|
+
// where available, probabilistic otherwise).
|
|
404
|
+
resolveDeferred: () => client.resolveDeferredDeepLink(),
|
|
428
405
|
onDeepLink,
|
|
429
406
|
});
|
|
430
407
|
// Cold-start resolution is fire-and-forget: a failure (Linking or the
|
package/src/native-types.ts
CHANGED
|
@@ -123,8 +123,6 @@ export interface AdvenueIosNativeModule {
|
|
|
123
123
|
getAdvertisingId(): Promise<string | null>;
|
|
124
124
|
/** Vendor-scoped device id (IDFV) — available without ATT; a fallback anchor. */
|
|
125
125
|
getIdentifierForVendor(): string | null;
|
|
126
|
-
/** Clipboard match token (advmatch:<id>) for deterministic deferred deep linking; null if absent. */
|
|
127
|
-
getPasteboardMatchToken(): string | null;
|
|
128
126
|
/**
|
|
129
127
|
* G3.2n — App Attest. Generates (or reuses a keychain-cached) DCAppAttest
|
|
130
128
|
* key, computes clientDataHash = SHA-256(challenge), and calls
|
package/src/native.ts
CHANGED
|
@@ -44,6 +44,29 @@ export function getPlatformOS(): string | null {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* OS *release* version for attribution matching: iOS `Platform.Version` is the
|
|
49
|
+
* system version string ('18.2'); Android `Platform.Version` is the API LEVEL
|
|
50
|
+
* (34) — sending it would break the server's normalized fingerprint (the
|
|
51
|
+
* click-side browser UA says "Android 14"), so Android reads
|
|
52
|
+
* `Platform.constants.Release` and degrades to null when it's absent.
|
|
53
|
+
*/
|
|
54
|
+
export function getOsVersion(): string | null {
|
|
55
|
+
try {
|
|
56
|
+
const rn = requireReactNative() as {
|
|
57
|
+
Platform: { OS: string; Version?: unknown; constants?: { Release?: unknown } };
|
|
58
|
+
} | null;
|
|
59
|
+
if (!rn) return null;
|
|
60
|
+
if (rn.Platform.OS === 'android') {
|
|
61
|
+
const release = rn.Platform.constants?.Release;
|
|
62
|
+
return release != null ? String(release) : null;
|
|
63
|
+
}
|
|
64
|
+
return rn.Platform.Version != null ? String(rn.Platform.Version) : null;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
47
70
|
/** Subscription handle returned by AppState.addEventListener. */
|
|
48
71
|
export interface AppStateSubscription {
|
|
49
72
|
remove(): void;
|