rn-prodeeplinks 0.0.1 → 0.0.3
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 +67 -16
- package/lib/api.d.ts +2 -2
- package/lib/api.js +11 -5
- package/lib/fingerprint.js +4 -2
- package/lib/index.d.ts +4 -2
- package/lib/index.js +53 -2
- package/lib/types.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
> ⚠️ **License Required**: This package requires a valid license key purchased from our portal. Without a license key, the package will not function.
|
|
6
6
|
|
|
7
|
-
> ℹ️ **Versioning**: Current version is 0.0.
|
|
7
|
+
> ℹ️ **Versioning**: Current version is 0.0.3 (in-progress). It will move to 1.x after stable.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -130,6 +130,45 @@ const deepLink = new ProDeepLink({
|
|
|
130
130
|
const result = await deepLink.getDeepLinkUrl();
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
### Analytics & Tracking
|
|
134
|
+
|
|
135
|
+
This SDK can optionally send deep link analytics events to our tracking service.
|
|
136
|
+
|
|
137
|
+
- When a deep link is opened via React Native `Linking.getInitialURL()`, the SDK:
|
|
138
|
+
- Resolves the URL
|
|
139
|
+
- Collects device fingerprint data
|
|
140
|
+
- Sends an internal analytics event with the resolved short URL and fingerprint
|
|
141
|
+
- When a deep link is resolved via the fingerprint API, the SDK:
|
|
142
|
+
- Resolves the URL from the server
|
|
143
|
+
- Sends the same internal analytics event with the short URL and fingerprint
|
|
144
|
+
|
|
145
|
+
These internal events are handled by the SDK and are not part of the public API.
|
|
146
|
+
|
|
147
|
+
You can also send custom tracking events manually after calling `init`:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import {
|
|
151
|
+
trackAnalyticsEvent,
|
|
152
|
+
CustomDeepLinkAnalyticsEvent,
|
|
153
|
+
} from 'rn-prodeeplinks';
|
|
154
|
+
|
|
155
|
+
const event: CustomDeepLinkAnalyticsEvent = {
|
|
156
|
+
eventType: 'deeplink',
|
|
157
|
+
eventName: 'my_custom_event',
|
|
158
|
+
category: 'custom',
|
|
159
|
+
action: 'open',
|
|
160
|
+
label: 'My custom event',
|
|
161
|
+
properties: {
|
|
162
|
+
shortUrl: 'https://your-short-url',
|
|
163
|
+
foo: 'bar',
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
await trackAnalyticsEvent(event);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The license key provided to `init` is automatically included in the request headers. You do not need to include the license key in the analytics event payload.
|
|
171
|
+
|
|
133
172
|
## API Reference
|
|
134
173
|
|
|
135
174
|
### Functions
|
|
@@ -182,6 +221,26 @@ getDeepLink((url) => {
|
|
|
182
221
|
});
|
|
183
222
|
```
|
|
184
223
|
|
|
224
|
+
#### `trackAnalyticsEvent(event: CustomDeepLinkAnalyticsEvent): Promise<any>`
|
|
225
|
+
|
|
226
|
+
Sends a custom analytics event to the tracking service.
|
|
227
|
+
|
|
228
|
+
`init()` must be called successfully first; the stored license key from `init` is sent in the request headers.
|
|
229
|
+
|
|
230
|
+
**Parameters:**
|
|
231
|
+
- `event` (CustomDeepLinkAnalyticsEvent, required): Event describing what happened. You can use any `eventName` and add any custom properties.
|
|
232
|
+
|
|
233
|
+
**Example:**
|
|
234
|
+
```typescript
|
|
235
|
+
await trackAnalyticsEvent({
|
|
236
|
+
eventType: 'deeplink',
|
|
237
|
+
eventName: 'button_click',
|
|
238
|
+
properties: {
|
|
239
|
+
buttonId: 'cta_start',
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
185
244
|
#### `isReady(): boolean`
|
|
186
245
|
|
|
187
246
|
Checks if the package has been initialized with a license key.
|
|
@@ -234,24 +293,16 @@ License keys are **NOT FREE**. You must:
|
|
|
234
293
|
- Invalid or expired license keys will result in API errors
|
|
235
294
|
- License keys cannot be shared or reused without authorization
|
|
236
295
|
|
|
237
|
-
### Custom License
|
|
238
|
-
For environments with a custom auth API, the license validate endpoint is:
|
|
239
|
-
|
|
240
|
-
POST {{base_url}}{{api_prefix}}/custom-deep-link/license/validate
|
|
296
|
+
### Custom License Validation (Optional)
|
|
241
297
|
|
|
242
|
-
|
|
298
|
+
In most cases you should rely on the built‑in license validation handled by this SDK.
|
|
299
|
+
If you have your own backend that integrates with our license service, you can:
|
|
243
300
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
--body '{
|
|
248
|
-
"licenseKey": "{{license_key}}",
|
|
249
|
-
"domain": "acme.com",
|
|
250
|
-
"ipAddress": "103.21.149.10"
|
|
251
|
-
}'
|
|
252
|
-
```
|
|
301
|
+
- Call your backend from your app
|
|
302
|
+
- Let your backend talk to our license service
|
|
303
|
+
- Use the result in your own business logic
|
|
253
304
|
|
|
254
|
-
|
|
305
|
+
The exact backend endpoint and payload are specific to your environment and are intentionally not exposed in this README.
|
|
255
306
|
|
|
256
307
|
## Error Handling
|
|
257
308
|
|
package/lib/api.d.ts
CHANGED
|
@@ -20,5 +20,5 @@ export declare function validateLicenseInit(licenseKey: string): Promise<{
|
|
|
20
20
|
status?: number;
|
|
21
21
|
data?: LicenseValidationApiResponse;
|
|
22
22
|
}>;
|
|
23
|
-
export declare function matchFingerprintCustom(payload: FingerprintMatchPayload, baseUrl?: string): Promise<FingerprintMatchResponse>;
|
|
24
|
-
export declare function trackCustomDeepLinkEvent(event: CustomDeepLinkAnalyticsEvent): Promise<any>;
|
|
23
|
+
export declare function matchFingerprintCustom(payload: FingerprintMatchPayload, baseUrl?: string, licenseKey?: string): Promise<FingerprintMatchResponse>;
|
|
24
|
+
export declare function trackCustomDeepLinkEvent(event: CustomDeepLinkAnalyticsEvent, licenseKey?: string): Promise<any>;
|
package/lib/api.js
CHANGED
|
@@ -26,7 +26,7 @@ async function fetchDeepLinkUrl(licenseKey, fingerprint, apiEndpoint, timeout =
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
const PACKAGE_NAME = 'react-native-pro-deeplink';
|
|
29
|
-
const PACKAGE_VERSION = '0.0.
|
|
29
|
+
const PACKAGE_VERSION = '0.0.3';
|
|
30
30
|
const payload = {
|
|
31
31
|
licenseKey,
|
|
32
32
|
fingerprint: fingerprint,
|
|
@@ -39,13 +39,14 @@ async function fetchDeepLinkUrl(licenseKey, fingerprint, apiEndpoint, timeout =
|
|
|
39
39
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
40
40
|
try {
|
|
41
41
|
const PACKAGE_NAME = 'react-native-pro-deeplink';
|
|
42
|
-
const PACKAGE_VERSION = '0.0.
|
|
42
|
+
const PACKAGE_VERSION = '0.0.3';
|
|
43
43
|
const response = await fetch(endpoint, {
|
|
44
44
|
method: 'POST',
|
|
45
45
|
headers: {
|
|
46
46
|
'Content-Type': 'application/json',
|
|
47
47
|
'X-Package-Name': PACKAGE_NAME,
|
|
48
48
|
'X-Package-Version': PACKAGE_VERSION,
|
|
49
|
+
'x-license-key': licenseKey,
|
|
49
50
|
},
|
|
50
51
|
body: JSON.stringify(payload),
|
|
51
52
|
signal: controller.signal,
|
|
@@ -129,7 +130,10 @@ async function validateLicenseCustom(licenseKey, opts) {
|
|
|
129
130
|
};
|
|
130
131
|
const res = await fetch(endpoint, {
|
|
131
132
|
method: 'POST',
|
|
132
|
-
headers: {
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
'x-license-key': licenseKey,
|
|
136
|
+
},
|
|
133
137
|
body: JSON.stringify(body),
|
|
134
138
|
});
|
|
135
139
|
const json = (await res.json().catch(() => ({})));
|
|
@@ -168,7 +172,7 @@ async function validateLicenseInit(licenseKey) {
|
|
|
168
172
|
};
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
|
-
async function matchFingerprintCustom(payload, baseUrl) {
|
|
175
|
+
async function matchFingerprintCustom(payload, baseUrl, licenseKey) {
|
|
172
176
|
try {
|
|
173
177
|
const base = (baseUrl || CUSTOM_API_BASE_URL).trim().replace(/\/+$/, '');
|
|
174
178
|
const endpoint = `${base}/custom-deep-link/fingerprint/match`;
|
|
@@ -176,6 +180,7 @@ async function matchFingerprintCustom(payload, baseUrl) {
|
|
|
176
180
|
method: 'POST',
|
|
177
181
|
headers: {
|
|
178
182
|
'Content-Type': 'application/json',
|
|
183
|
+
...(licenseKey ? { 'x-license-key': licenseKey } : {}),
|
|
179
184
|
},
|
|
180
185
|
body: JSON.stringify(payload),
|
|
181
186
|
});
|
|
@@ -190,12 +195,13 @@ async function matchFingerprintCustom(payload, baseUrl) {
|
|
|
190
195
|
};
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
|
-
async function trackCustomDeepLinkEvent(event) {
|
|
198
|
+
async function trackCustomDeepLinkEvent(event, licenseKey) {
|
|
194
199
|
try {
|
|
195
200
|
const res = await fetch(ANALYTICS_ENDPOINT, {
|
|
196
201
|
method: 'POST',
|
|
197
202
|
headers: {
|
|
198
203
|
'Content-Type': 'application/json',
|
|
204
|
+
...(licenseKey ? { 'x-license-key': licenseKey } : {}),
|
|
199
205
|
},
|
|
200
206
|
body: JSON.stringify(event),
|
|
201
207
|
});
|
package/lib/fingerprint.js
CHANGED
|
@@ -26,8 +26,10 @@ async function generateDeviceFingerprint() {
|
|
|
26
26
|
const isRooted = react_native_1.Platform.OS === 'android'
|
|
27
27
|
? (await (react_native_device_info_1.default.isDeviceRooted?.() ?? false))
|
|
28
28
|
: false;
|
|
29
|
-
|
|
30
|
-
const locale =
|
|
29
|
+
const deviceLocales = react_native_device_info_1.default.getDeviceLocales?.();
|
|
30
|
+
const locale = (Array.isArray(deviceLocales) && deviceLocales.length > 0
|
|
31
|
+
? deviceLocales[0]
|
|
32
|
+
: Intl.DateTimeFormat().resolvedOptions().locale) || 'en';
|
|
31
33
|
const language = (locale || '').split('-')[0] || 'en';
|
|
32
34
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33
35
|
// Get network info
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InitConfig, DeepLinkResponse } from './types';
|
|
1
|
+
import { InitConfig, DeepLinkResponse, CustomDeepLinkAnalyticsEvent } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Initialize the deep link package with license key
|
|
4
4
|
* This must be called before using getDeepLink()
|
|
@@ -53,7 +53,8 @@ export declare function isReady(): boolean;
|
|
|
53
53
|
* Useful for testing or logout scenarios
|
|
54
54
|
*/
|
|
55
55
|
export declare function reset(): void;
|
|
56
|
-
export
|
|
56
|
+
export declare function trackAnalyticsEvent(event: CustomDeepLinkAnalyticsEvent): Promise<any>;
|
|
57
|
+
export type { InitConfig, DeepLinkResponse, CustomDeepLinkAnalyticsEvent } from './types';
|
|
57
58
|
export declare class ProDeepLink {
|
|
58
59
|
private licenseKey;
|
|
59
60
|
constructor(config: InitConfig);
|
|
@@ -64,5 +65,6 @@ declare const _default: {
|
|
|
64
65
|
getDeepLink: typeof getDeepLink;
|
|
65
66
|
isReady: typeof isReady;
|
|
66
67
|
reset: typeof reset;
|
|
68
|
+
trackAnalyticsEvent: typeof trackAnalyticsEvent;
|
|
67
69
|
};
|
|
68
70
|
export default _default;
|
package/lib/index.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.init = init;
|
|
|
5
5
|
exports.getDeepLink = getDeepLink;
|
|
6
6
|
exports.isReady = isReady;
|
|
7
7
|
exports.reset = reset;
|
|
8
|
+
exports.trackAnalyticsEvent = trackAnalyticsEvent;
|
|
8
9
|
const fingerprint_1 = require("./fingerprint");
|
|
9
10
|
const api_1 = require("./api");
|
|
10
11
|
const license_1 = require("./license");
|
|
@@ -60,6 +61,25 @@ async function init(config) {
|
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
async function trackDeepLinkResolved(url, source, fingerprint) {
|
|
65
|
+
const event = {
|
|
66
|
+
eventType: 'deeplink',
|
|
67
|
+
eventName: 'pro_track',
|
|
68
|
+
category: source,
|
|
69
|
+
action: 'open',
|
|
70
|
+
label: url,
|
|
71
|
+
properties: {
|
|
72
|
+
shortUrl: url,
|
|
73
|
+
source,
|
|
74
|
+
fingerprint,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
await trackAnalyticsEvent(event);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
63
83
|
/**
|
|
64
84
|
* Get deep link URL from server
|
|
65
85
|
* This function automatically handles device fingerprinting internally
|
|
@@ -95,6 +115,12 @@ async function getDeepLink(callback) {
|
|
|
95
115
|
// First: try to read deep link via Linking (if app opened by URL)
|
|
96
116
|
const initialUrl = await react_native_1.Linking.getInitialURL();
|
|
97
117
|
if (initialUrl) {
|
|
118
|
+
try {
|
|
119
|
+
const fingerprint = await (0, fingerprint_1.generateDeviceFingerprint)();
|
|
120
|
+
await trackDeepLinkResolved(initialUrl, 'linking', fingerprint);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
}
|
|
98
124
|
if (callback)
|
|
99
125
|
callback(initialUrl);
|
|
100
126
|
return { success: true, url: initialUrl };
|
|
@@ -104,6 +130,13 @@ async function getDeepLink(callback) {
|
|
|
104
130
|
// Fetch deep link URL from API with retry mechanism
|
|
105
131
|
const result = await (0, api_1.fetchDeepLinkUrlWithRetry)(storedLicenseKey, fingerprint, 3, // retry attempts
|
|
106
132
|
DEFAULT_API_ENDPOINT);
|
|
133
|
+
if (result.success && result.url) {
|
|
134
|
+
try {
|
|
135
|
+
await trackDeepLinkResolved(result.url, 'api', fingerprint);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
}
|
|
139
|
+
}
|
|
107
140
|
// Call callback if provided and result is successful
|
|
108
141
|
if (callback && result.success && result.url) {
|
|
109
142
|
callback(result.url);
|
|
@@ -136,6 +169,16 @@ function reset() {
|
|
|
136
169
|
storedLicenseKey = null;
|
|
137
170
|
isInitialized = false;
|
|
138
171
|
}
|
|
172
|
+
async function trackAnalyticsEvent(event) {
|
|
173
|
+
if (!isInitialized || !storedLicenseKey) {
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: 'Please call init() first with your license key',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const { licenseKey: _ignored, ...payload } = event;
|
|
180
|
+
return (0, api_1.trackCustomDeepLinkEvent)(payload, storedLicenseKey);
|
|
181
|
+
}
|
|
139
182
|
// Keep backward compatibility - export class for advanced users (optional)
|
|
140
183
|
class ProDeepLink {
|
|
141
184
|
constructor(config) {
|
|
@@ -154,9 +197,17 @@ class ProDeepLink {
|
|
|
154
197
|
};
|
|
155
198
|
}
|
|
156
199
|
const fingerprint = await (0, fingerprint_1.generateDeviceFingerprint)();
|
|
157
|
-
|
|
200
|
+
const result = await (0, api_1.fetchDeepLinkUrlWithRetry)(this.licenseKey, fingerprint, 3, DEFAULT_API_ENDPOINT);
|
|
201
|
+
if (result.success && result.url) {
|
|
202
|
+
try {
|
|
203
|
+
await trackDeepLinkResolved(result.url, 'api', fingerprint);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
158
209
|
}
|
|
159
210
|
}
|
|
160
211
|
exports.ProDeepLink = ProDeepLink;
|
|
161
212
|
// Export default
|
|
162
|
-
exports.default = { init, getDeepLink, isReady, reset };
|
|
213
|
+
exports.default = { init, getDeepLink, isReady, reset, trackAnalyticsEvent };
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED