rn-prodeeplinks 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,330 @@
1
+ # rn-prodeeplinks
2
+
3
+ **PAID PACKAGE** - Secure deep linking package for React Native with license key validation and device fingerprinting.
4
+
5
+ > âš ī¸ **License Required**: This package requires a valid license key purchased from our portal. Without a license key, the package will not function.
6
+
7
+ > â„šī¸ **Versioning**: Current version is 0.0.1 (in-progress). It will move to 1.x after stable.
8
+
9
+ ## Features
10
+
11
+ - 🔐 **License Key Protection** - Secure license key validation
12
+ - 🔗 **Deep Link Management** - Server-side deep link URL management
13
+ - đŸ›Ąī¸ **Code Protection** - Built-in protection against code extraction
14
+ - ⚡ **Retry Mechanism** - Automatic retry with exponential backoff
15
+ - 📊 **TypeScript Support** - Full TypeScript definitions included
16
+
17
+ ## Getting Started
18
+
19
+ ### Step 1: Purchase License Key
20
+
21
+ Before using this package, you must purchase a license key from our portal. The license key will be provided after successful payment.
22
+
23
+ ### Step 2: Installation
24
+
25
+ ```bash
26
+ npm install rn-prodeeplinks
27
+ ```
28
+
29
+ ### Step 3: Install Peer Dependencies
30
+
31
+ This package requires the following peer dependencies:
32
+
33
+ ```bash
34
+ npm install react-native-device-info @react-native-community/netinfo
35
+ ```
36
+
37
+ ## Usage
38
+ ### Deep Link Resolution Flow
39
+ - First checks React Native Linking for an initial URL (if app opened via deep link).
40
+ - If none, calls fingerprint-based API to resolve server-managed deep link.
41
+ - If neither returns a URL, returns null.
42
+
43
+ ### Basic Usage
44
+
45
+ ```typescript
46
+ import { init, getDeepLink } from 'rn-prodeeplinks';
47
+
48
+ // Step 1: Initialize with your license key (obtained from our portal after payment)
49
+ const initResult = await init({
50
+ licenseKey: 'your-license-key-from-portal' // REQUIRED: Purchase from our portal
51
+ });
52
+
53
+ if (!initResult.success) {
54
+ console.error('Initialization failed:', initResult.error);
55
+ return;
56
+ }
57
+
58
+ // Step 2: Get deep link URL (Linking-first, then API, else null)
59
+ const result = await getDeepLink();
60
+
61
+ if (result.success && result.url) {
62
+ // Use the URL for deep linking
63
+ console.log('Deep link URL:', result.url);
64
+ // Use Linking.openURL(result.url) or your preferred method
65
+ } else {
66
+ // Either API error or flow returned null
67
+ console.log('No deep link available or error:', result.error);
68
+ }
69
+ ```
70
+
71
+ ### Using Callback
72
+
73
+ ```typescript
74
+ import { init, getDeepLink } from 'rn-prodeeplinks';
75
+
76
+ // Initialize once
77
+ await init({ licenseKey: 'your-license-key-here' });
78
+
79
+ // Get deep link with callback
80
+ getDeepLink((url) => {
81
+ console.log('Deep link URL:', url);
82
+ // Handle the deep link URL
83
+ });
84
+ ```
85
+
86
+ ### Complete Example
87
+
88
+ ```typescript
89
+ import { init, getDeepLink, isReady } from 'rn-prodeeplinks';
90
+ import { Linking } from 'react-native';
91
+
92
+ async function setupDeepLink() {
93
+ // Initialize with license key
94
+ const initResult = await init({
95
+ licenseKey: 'your-license-key-here'
96
+ });
97
+
98
+ if (!initResult.success) {
99
+ console.error('Failed to initialize:', initResult.error);
100
+ return;
101
+ }
102
+
103
+ // Check if ready
104
+ if (isReady()) {
105
+ // Get deep link
106
+ const result = await getDeepLink();
107
+
108
+ if (result.success && result.url) {
109
+ // Open the deep link
110
+ await Linking.openURL(result.url);
111
+ } else {
112
+ // If null, there is no deep link to process
113
+ console.log('Deep link unavailable:', result.error);
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### Advanced Usage (Class-based - Optional)
120
+
121
+ For advanced users who prefer class-based approach:
122
+
123
+ ```typescript
124
+ import { ProDeepLink } from 'rn-prodeeplinks';
125
+
126
+ const deepLink = new ProDeepLink({
127
+ licenseKey: 'your-license-key-here'
128
+ });
129
+
130
+ const result = await deepLink.getDeepLinkUrl();
131
+ ```
132
+
133
+ ## API Reference
134
+
135
+ ### Functions
136
+
137
+ #### `init(config: InitConfig): Promise<{ success: boolean; error?: string }>`
138
+
139
+ Initializes the package with your license key. This must be called before using `getDeepLink()`.
140
+
141
+ **Parameters:**
142
+ - `config.licenseKey` (string, required): Your license key from the portal
143
+
144
+ **Returns:**
145
+ ```typescript
146
+ {
147
+ success: boolean;
148
+ error?: string;
149
+ }
150
+ ```
151
+
152
+ **Example:**
153
+ ```typescript
154
+ const result = await init({ licenseKey: 'your-license-key' });
155
+ ```
156
+
157
+ #### `getDeepLink(callback?: (url: string) => void): Promise<DeepLinkResponse>`
158
+
159
+ Fetches the deep link URL from the server.
160
+
161
+ **Parameters:**
162
+ - `callback` (optional): Callback function that receives the deep link URL when successful
163
+
164
+ **Returns:**
165
+ ```typescript
166
+ {
167
+ success: boolean;
168
+ url?: string | null;
169
+ message?: string;
170
+ error?: string;
171
+ }
172
+ ```
173
+
174
+ **Example:**
175
+ ```typescript
176
+ // Using promise
177
+ const result = await getDeepLink();
178
+
179
+ // Using callback
180
+ getDeepLink((url) => {
181
+ console.log('Deep link:', url);
182
+ });
183
+ ```
184
+
185
+ #### `isReady(): boolean`
186
+
187
+ Checks if the package has been initialized with a license key.
188
+
189
+ **Returns:** `boolean`
190
+
191
+ #### `reset(): void`
192
+
193
+ Resets/clears the stored license key. Useful for testing or logout scenarios.
194
+
195
+ ### ProDeepLink Class (Advanced - Optional)
196
+
197
+ For users who prefer class-based approach:
198
+
199
+ #### Constructor
200
+
201
+ ```typescript
202
+ new ProDeepLink(config: InitConfig)
203
+ ```
204
+
205
+ **Config Options:**
206
+ - `licenseKey` (string, required): Your license key
207
+
208
+ #### Methods
209
+
210
+ ##### `getDeepLinkUrl(): Promise<DeepLinkResponse>`
211
+
212
+ Fetches the deep link URL from the server.
213
+
214
+ ## Security Features
215
+
216
+ 1. **License Key Protection** - Secure license key validation and obfuscation
217
+ 2. **Server-Side Validation** - All license validation happens on our secure server
218
+ 3. **Secure API Endpoint** - All communication happens through our encrypted API endpoint
219
+ 4. **Code Protection** - Built-in measures to prevent code extraction
220
+
221
+ ## License Key
222
+
223
+ ### Obtaining a License Key
224
+
225
+ License keys are **NOT FREE**. You must:
226
+ 1. Visit our payment portal
227
+ 2. Complete the purchase process
228
+ 3. Receive your unique license key via email/portal
229
+ 4. Use the license key to initialize this package
230
+
231
+ ### License Validation
232
+
233
+ - License keys are validated on **every API call** to our server
234
+ - Invalid or expired license keys will result in API errors
235
+ - License keys cannot be shared or reused without authorization
236
+
237
+ ### Custom License Validate API (Optional)
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
241
+
242
+ Example (Postman):
243
+
244
+ ```
245
+ POST '{{base_url}}{{api_prefix}}/custom-deep-link/license/validate'
246
+ --header 'Content-Type: application/json'
247
+ --body '{
248
+ "licenseKey": "{{license_key}}",
249
+ "domain": "acme.com",
250
+ "ipAddress": "103.21.149.10"
251
+ }'
252
+ ```
253
+
254
+ This package exposes a helper to call this endpoint if needed. It does not interfere with the main deep link resolution flow.
255
+
256
+ ## Error Handling
257
+
258
+ The package handles various error scenarios:
259
+
260
+ - Invalid license key
261
+ - Network timeouts
262
+ - API errors
263
+
264
+ All errors are returned in the `DeepLinkResponse` object with descriptive error messages.
265
+
266
+ ## TypeScript Support
267
+
268
+ Full TypeScript definitions are included. Import types as needed:
269
+
270
+ ```typescript
271
+ import { InitConfig, DeepLinkResponse } from 'rn-prodeeplinks';
272
+ ```
273
+
274
+ **Available Types:**
275
+ - `InitConfig` - Configuration for initialization
276
+ - `DeepLinkResponse` - Response from getDeepLink()
277
+
278
+ ## Troubleshooting
279
+
280
+ ### License Key Errors
281
+
282
+ If you receive license key errors:
283
+ 1. Ensure you've called `init()` before using `getDeepLink()`
284
+ 2. Check that the license key is valid on our portal
285
+ 3. Verify the license key hasn't expired
286
+ 4. Make sure you're using the correct license key
287
+
288
+ ### Network Errors
289
+
290
+ If you encounter network errors:
291
+ 1. Check your internet connection
292
+ 2. Verify you have proper network permissions
293
+ 3. Check if our API endpoint is accessible from your network
294
+ 4. Try again after a few moments (automatic retry is built-in)
295
+
296
+ ### Initialization Errors
297
+
298
+ If initialization fails:
299
+ 1. Make sure you're calling `init()` before `getDeepLink()`
300
+ 2. Verify the license key is correct
301
+ 3. Check that `isReady()` returns `true` before making API calls
302
+
303
+ ## Payment & Licensing
304
+
305
+ This is a **paid package**. To use this package:
306
+
307
+ 1. **Purchase**: Visit our payment portal and purchase a license
308
+ 2. **Get License Key**: Receive your unique license key after payment
309
+ 3. **Use Package**: Initialize the package with your license key using `init()`
310
+ 4. **Server Validation**: Our server validates the license key on each request
311
+
312
+ ### Important Notes
313
+
314
+ - ❌ **No license key = Package will not work**
315
+ - ❌ **Invalid license key = API calls will fail**
316
+ - ❌ **Expired license key = Access denied**
317
+ - ❌ **Not initialized = getDeepLink() will fail**
318
+ - ✅ **Valid license key + init() = Full access to all features**
319
+ - ✅ **API endpoint = Managed by us (no setup required)**
320
+
321
+ ## Support
322
+
323
+ For support regarding:
324
+ - **License keys**: Contact our payment portal support
325
+ - **Technical issues**: Check the documentation or contact technical support
326
+ - **Payment issues**: Contact billing support
327
+
328
+ ## License
329
+
330
+ This package is proprietary software. Unauthorized copying, distribution, or use without a valid license key is prohibited and may result in legal action.
package/lib/api.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { DeepLinkResponse, DeviceFingerprint, FingerprintMatchPayload, CustomDeepLinkAnalyticsEvent, LicenseValidationApiResponse, FingerprintMatchResponse } from './types';
2
+ /**
3
+ * Call API to get deep link URL
4
+ * This function sends device fingerprint and license key to server
5
+ */
6
+ export declare function fetchDeepLinkUrl(licenseKey: string, fingerprint: DeviceFingerprint, apiEndpoint?: string, timeout?: number): Promise<DeepLinkResponse>;
7
+ /**
8
+ * Retry mechanism for API calls
9
+ */
10
+ export declare function fetchDeepLinkUrlWithRetry(licenseKey: string, fingerprint: DeviceFingerprint, retryAttempts?: number, apiEndpoint?: string, timeout?: number): Promise<DeepLinkResponse>;
11
+ export declare function validateLicenseCustom(licenseKey: string, opts?: {
12
+ baseUrl?: string;
13
+ apiPrefix?: string;
14
+ domain?: string;
15
+ ipAddress?: string;
16
+ }): Promise<LicenseValidationApiResponse>;
17
+ export declare function validateLicenseInit(licenseKey: string): Promise<{
18
+ success: boolean;
19
+ error?: string;
20
+ status?: number;
21
+ data?: LicenseValidationApiResponse;
22
+ }>;
23
+ export declare function matchFingerprintCustom(payload: FingerprintMatchPayload, baseUrl?: string): Promise<FingerprintMatchResponse>;
24
+ export declare function trackCustomDeepLinkEvent(event: CustomDeepLinkAnalyticsEvent): Promise<any>;
package/lib/api.js ADDED
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchDeepLinkUrl = fetchDeepLinkUrl;
4
+ exports.fetchDeepLinkUrlWithRetry = fetchDeepLinkUrlWithRetry;
5
+ exports.validateLicenseCustom = validateLicenseCustom;
6
+ exports.validateLicenseInit = validateLicenseInit;
7
+ exports.matchFingerprintCustom = matchFingerprintCustom;
8
+ exports.trackCustomDeepLinkEvent = trackCustomDeepLinkEvent;
9
+ const license_1 = require("./license");
10
+ const DEFAULT_API_ENDPOINT = 'https://api.prodeeplink.com/v1/deeplink';
11
+ const CUSTOM_API_BASE_URL = 'https://api.prodeeplinks.com';
12
+ const ANALYTICS_ENDPOINT = 'https://s.finprim.com/custom-deep-link/track/event';
13
+ /**
14
+ * Call API to get deep link URL
15
+ * This function sends device fingerprint and license key to server
16
+ */
17
+ async function fetchDeepLinkUrl(licenseKey, fingerprint, apiEndpoint, timeout = 10000) {
18
+ const endpoint = apiEndpoint || DEFAULT_API_ENDPOINT;
19
+ try {
20
+ // Validate license key format first
21
+ const validation = (0, license_1.validateLicenseKeyFormat)(licenseKey);
22
+ if (!validation.isValid) {
23
+ return {
24
+ success: false,
25
+ error: validation.message || 'Invalid license key',
26
+ };
27
+ }
28
+ const PACKAGE_NAME = 'react-native-pro-deeplink';
29
+ const PACKAGE_VERSION = '0.0.1';
30
+ const payload = {
31
+ licenseKey,
32
+ fingerprint: fingerprint,
33
+ packageName: PACKAGE_NAME,
34
+ packageVersion: PACKAGE_VERSION,
35
+ timestamp: Date.now(),
36
+ };
37
+ // Make API call with timeout
38
+ const controller = new AbortController();
39
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
40
+ try {
41
+ const PACKAGE_NAME = 'react-native-pro-deeplink';
42
+ const PACKAGE_VERSION = '0.0.1';
43
+ const response = await fetch(endpoint, {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ 'X-Package-Name': PACKAGE_NAME,
48
+ 'X-Package-Version': PACKAGE_VERSION,
49
+ },
50
+ body: JSON.stringify(payload),
51
+ signal: controller.signal,
52
+ });
53
+ clearTimeout(timeoutId);
54
+ if (!response.ok) {
55
+ const errorData = await response.json().catch(() => ({}));
56
+ return {
57
+ success: false,
58
+ error: errorData.message || `API error: ${response.status}`,
59
+ };
60
+ }
61
+ const data = await response.json();
62
+ if (data.success && data.url) {
63
+ return {
64
+ success: true,
65
+ url: data.url,
66
+ message: data.message,
67
+ };
68
+ }
69
+ else {
70
+ return {
71
+ success: false,
72
+ error: data.message || 'No URL returned from API',
73
+ };
74
+ }
75
+ }
76
+ catch (fetchError) {
77
+ clearTimeout(timeoutId);
78
+ if (fetchError.name === 'AbortError') {
79
+ return {
80
+ success: false,
81
+ error: 'Request timeout',
82
+ };
83
+ }
84
+ throw fetchError;
85
+ }
86
+ }
87
+ catch (error) {
88
+ console.error('Error fetching deep link URL:', error);
89
+ return {
90
+ success: false,
91
+ error: error.message || 'Failed to fetch deep link URL',
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Retry mechanism for API calls
97
+ */
98
+ async function fetchDeepLinkUrlWithRetry(licenseKey, fingerprint, retryAttempts = 3, apiEndpoint, timeout = 10000) {
99
+ let lastError = null;
100
+ for (let attempt = 1; attempt <= retryAttempts; attempt++) {
101
+ const result = await fetchDeepLinkUrl(licenseKey, fingerprint, apiEndpoint, timeout);
102
+ if (result.success) {
103
+ return result;
104
+ }
105
+ lastError = result;
106
+ // Don't retry on license validation errors
107
+ if (result.error?.includes('license') || result.error?.includes('Invalid')) {
108
+ return result;
109
+ }
110
+ // Wait before retry (exponential backoff)
111
+ if (attempt < retryAttempts) {
112
+ await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
113
+ }
114
+ }
115
+ return lastError || {
116
+ success: false,
117
+ error: 'Failed after retries',
118
+ };
119
+ }
120
+ async function validateLicenseCustom(licenseKey, opts) {
121
+ try {
122
+ const base = (opts?.baseUrl || '').trim();
123
+ const prefix = (opts?.apiPrefix || '').trim();
124
+ const endpoint = `${base}${prefix}/custom-deep-link/license/validate`;
125
+ const body = {
126
+ licenseKey,
127
+ domain: opts?.domain || '',
128
+ ipAddress: opts?.ipAddress || '',
129
+ };
130
+ const res = await fetch(endpoint, {
131
+ method: 'POST',
132
+ headers: { 'Content-Type': 'application/json' },
133
+ body: JSON.stringify(body),
134
+ });
135
+ const json = (await res.json().catch(() => ({})));
136
+ return json;
137
+ }
138
+ catch (e) {
139
+ return { success: false, error: e?.message || 'License validate failed' };
140
+ }
141
+ }
142
+ async function validateLicenseInit(licenseKey) {
143
+ try {
144
+ const endpoint = `${CUSTOM_API_BASE_URL}/custom-deep-link/license/validate`;
145
+ const res = await fetch(endpoint, {
146
+ method: 'POST',
147
+ headers: {
148
+ 'Content-Type': 'application/json',
149
+ 'x-license-key': licenseKey,
150
+ },
151
+ body: JSON.stringify({ licenseKey }),
152
+ });
153
+ const data = (await res.json().catch(() => ({})));
154
+ if (!res.ok) {
155
+ const message = data?.message || data?.error || 'License validation failed';
156
+ return { success: false, error: message, status: res.status, data };
157
+ }
158
+ if (!data.success || !data.valid) {
159
+ const message = data?.message || data?.error || 'License is not valid';
160
+ return { success: false, error: message, status: res.status, data };
161
+ }
162
+ return { success: true, status: res.status, data };
163
+ }
164
+ catch (e) {
165
+ return {
166
+ success: false,
167
+ error: e?.message || 'License validation request failed',
168
+ };
169
+ }
170
+ }
171
+ async function matchFingerprintCustom(payload, baseUrl) {
172
+ try {
173
+ const base = (baseUrl || CUSTOM_API_BASE_URL).trim().replace(/\/+$/, '');
174
+ const endpoint = `${base}/custom-deep-link/fingerprint/match`;
175
+ const res = await fetch(endpoint, {
176
+ method: 'POST',
177
+ headers: {
178
+ 'Content-Type': 'application/json',
179
+ },
180
+ body: JSON.stringify(payload),
181
+ });
182
+ const data = (await res.json().catch(() => ({})));
183
+ return data;
184
+ }
185
+ catch (e) {
186
+ return {
187
+ matched: false,
188
+ matchConfidence: 0,
189
+ ...(e ? { error: e?.message || 'Fingerprint match failed' } : {}),
190
+ };
191
+ }
192
+ }
193
+ async function trackCustomDeepLinkEvent(event) {
194
+ try {
195
+ const res = await fetch(ANALYTICS_ENDPOINT, {
196
+ method: 'POST',
197
+ headers: {
198
+ 'Content-Type': 'application/json',
199
+ },
200
+ body: JSON.stringify(event),
201
+ });
202
+ const data = await res.json().catch(() => ({}));
203
+ return data;
204
+ }
205
+ catch (e) {
206
+ return { success: false, error: e?.message || 'Analytics event tracking failed' };
207
+ }
208
+ }
@@ -0,0 +1,6 @@
1
+ import { DeviceFingerprint } from './types';
2
+ /**
3
+ * Generate device fingerprint for deep link matching
4
+ * Collects all device information required for fingerprint matching
5
+ */
6
+ export declare function generateDeviceFingerprint(): Promise<DeviceFingerprint>;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateDeviceFingerprint = generateDeviceFingerprint;
7
+ const react_native_device_info_1 = __importDefault(require("react-native-device-info"));
8
+ const react_native_1 = require("react-native");
9
+ const netinfo_1 = __importDefault(require("@react-native-community/netinfo"));
10
+ /**
11
+ * Generate device fingerprint for deep link matching
12
+ * Collects all device information required for fingerprint matching
13
+ */
14
+ async function generateDeviceFingerprint() {
15
+ try {
16
+ const { width, height } = react_native_1.Dimensions.get('window');
17
+ const screenResolution = `${width}x${height}`;
18
+ // Get device info
19
+ const deviceId = await react_native_device_info_1.default.getUniqueId();
20
+ const deviceModel = react_native_device_info_1.default.getModel();
21
+ const manufacturer = react_native_1.Platform.OS === 'android' ? await react_native_device_info_1.default.getManufacturer() : 'Apple';
22
+ const osVersion = react_native_device_info_1.default.getSystemVersion();
23
+ const appVersion = react_native_device_info_1.default.getVersion();
24
+ const buildNumber = react_native_device_info_1.default.getBuildNumber();
25
+ const isSimulator = await react_native_device_info_1.default.isEmulator();
26
+ const isRooted = react_native_1.Platform.OS === 'android'
27
+ ? (await (react_native_device_info_1.default.isDeviceRooted?.() ?? false))
28
+ : false;
29
+ // Get locale info
30
+ const locale = react_native_device_info_1.default.getDeviceLocale?.() || Intl.DateTimeFormat().resolvedOptions().locale;
31
+ const language = (locale || '').split('-')[0] || 'en';
32
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
33
+ // Get network info
34
+ const netInfo = await netinfo_1.default.fetch();
35
+ const connectionType = netInfo.type;
36
+ const carrier = react_native_1.Platform.OS === 'ios' ? await react_native_device_info_1.default.getCarrier() : undefined;
37
+ // Get IP address (if available)
38
+ let ipAddress;
39
+ try {
40
+ // Prefer device info API if available
41
+ ipAddress = await (react_native_device_info_1.default.getIpAddress?.() ?? undefined);
42
+ // Fallback to network info if not available
43
+ if (!ipAddress && netInfo.details && 'ipAddress' in netInfo.details) {
44
+ ipAddress = netInfo.details.ipAddress;
45
+ }
46
+ }
47
+ catch (error) {
48
+ console.warn('Could not get IP address:', error);
49
+ }
50
+ const fingerprint = {
51
+ platform: react_native_1.Platform.OS,
52
+ osVersion,
53
+ deviceId,
54
+ deviceModel,
55
+ manufacturer,
56
+ screenResolution,
57
+ screenWidth: width,
58
+ screenHeight: height,
59
+ timezone,
60
+ language,
61
+ locale,
62
+ appVersion: `${appVersion} (${buildNumber})`,
63
+ carrier,
64
+ connectionType: connectionType || undefined,
65
+ isSimulator,
66
+ isRooted,
67
+ ipAddress,
68
+ };
69
+ return fingerprint;
70
+ }
71
+ catch (error) {
72
+ console.error('Error generating device fingerprint:', error);
73
+ // Return minimal fingerprint on error
74
+ const { width, height } = react_native_1.Dimensions.get('window');
75
+ return {
76
+ platform: react_native_1.Platform.OS,
77
+ osVersion: react_native_1.Platform.Version.toString(),
78
+ deviceId: 'unknown',
79
+ deviceModel: 'unknown',
80
+ screenResolution: `${width}x${height}`,
81
+ screenWidth: width,
82
+ screenHeight: height,
83
+ appVersion: 'unknown',
84
+ };
85
+ }
86
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,68 @@
1
+ import { InitConfig, DeepLinkResponse } from './types';
2
+ /**
3
+ * Initialize the deep link package with license key
4
+ * This must be called before using getDeepLink()
5
+ *
6
+ * @param config - Configuration object containing license key
7
+ * @returns Object with success status and optional error message
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { init } from 'rn-prodeeplinks';
12
+ *
13
+ * const result = await init({ licenseKey: 'your-license-key-here' });
14
+ * if (result.success) {
15
+ * console.log('Initialized successfully');
16
+ * }
17
+ * ```
18
+ */
19
+ export declare function init(config: InitConfig): Promise<{
20
+ success: boolean;
21
+ error?: string;
22
+ }>;
23
+ /**
24
+ * Get deep link URL from server
25
+ * This function automatically handles device fingerprinting internally
26
+ *
27
+ * @param callback - Optional callback function that receives the deep link URL
28
+ * @returns Promise with deep link response
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { getDeepLink } from 'rn-prodeeplinks';
33
+ *
34
+ * // Using promise
35
+ * const result = await getDeepLink();
36
+ * if (result.success && result.url) {
37
+ * console.log('Deep link:', result.url);
38
+ * }
39
+ *
40
+ * // Using callback
41
+ * getDeepLink((url) => {
42
+ * console.log('Deep link:', url);
43
+ * });
44
+ * ```
45
+ */
46
+ export declare function getDeepLink(callback?: (url: string) => void): Promise<DeepLinkResponse>;
47
+ /**
48
+ * Check if the package is initialized
49
+ */
50
+ export declare function isReady(): boolean;
51
+ /**
52
+ * Reset/clear the stored license key
53
+ * Useful for testing or logout scenarios
54
+ */
55
+ export declare function reset(): void;
56
+ export type { InitConfig, DeepLinkResponse } from './types';
57
+ export declare class ProDeepLink {
58
+ private licenseKey;
59
+ constructor(config: InitConfig);
60
+ getDeepLinkUrl(): Promise<DeepLinkResponse>;
61
+ }
62
+ declare const _default: {
63
+ init: typeof init;
64
+ getDeepLink: typeof getDeepLink;
65
+ isReady: typeof isReady;
66
+ reset: typeof reset;
67
+ };
68
+ export default _default;
package/lib/index.js ADDED
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProDeepLink = void 0;
4
+ exports.init = init;
5
+ exports.getDeepLink = getDeepLink;
6
+ exports.isReady = isReady;
7
+ exports.reset = reset;
8
+ const fingerprint_1 = require("./fingerprint");
9
+ const api_1 = require("./api");
10
+ const license_1 = require("./license");
11
+ const react_native_1 = require("react-native");
12
+ // Global state to store license key and configuration
13
+ let storedLicenseKey = null;
14
+ let isInitialized = false;
15
+ // Hardcoded API endpoint - user doesn't need to know about this
16
+ const DEFAULT_API_ENDPOINT = 'https://api.prodeeplink.com/v1/deeplink';
17
+ /**
18
+ * Initialize the deep link package with license key
19
+ * This must be called before using getDeepLink()
20
+ *
21
+ * @param config - Configuration object containing license key
22
+ * @returns Object with success status and optional error message
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { init } from 'rn-prodeeplinks';
27
+ *
28
+ * const result = await init({ licenseKey: 'your-license-key-here' });
29
+ * if (result.success) {
30
+ * console.log('Initialized successfully');
31
+ * }
32
+ * ```
33
+ */
34
+ async function init(config) {
35
+ try {
36
+ const validation = (0, license_1.validateLicenseKeyFormat)(config.licenseKey);
37
+ if (!validation.isValid) {
38
+ return {
39
+ success: false,
40
+ error: validation.message || 'Invalid license key',
41
+ };
42
+ }
43
+ const remoteValidation = await (0, api_1.validateLicenseInit)(config.licenseKey);
44
+ if (!remoteValidation.success) {
45
+ return {
46
+ success: false,
47
+ error: remoteValidation.error || 'License validation failed',
48
+ };
49
+ }
50
+ storedLicenseKey = config.licenseKey;
51
+ isInitialized = true;
52
+ return {
53
+ success: true,
54
+ };
55
+ }
56
+ catch (error) {
57
+ return {
58
+ success: false,
59
+ error: error.message || 'Failed to initialize',
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * Get deep link URL from server
65
+ * This function automatically handles device fingerprinting internally
66
+ *
67
+ * @param callback - Optional callback function that receives the deep link URL
68
+ * @returns Promise with deep link response
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * import { getDeepLink } from 'rn-prodeeplinks';
73
+ *
74
+ * // Using promise
75
+ * const result = await getDeepLink();
76
+ * if (result.success && result.url) {
77
+ * console.log('Deep link:', result.url);
78
+ * }
79
+ *
80
+ * // Using callback
81
+ * getDeepLink((url) => {
82
+ * console.log('Deep link:', url);
83
+ * });
84
+ * ```
85
+ */
86
+ async function getDeepLink(callback) {
87
+ // Check if initialized
88
+ if (!isInitialized || !storedLicenseKey) {
89
+ return {
90
+ success: false,
91
+ error: 'Please call init() first with your license key',
92
+ };
93
+ }
94
+ try {
95
+ // First: try to read deep link via Linking (if app opened by URL)
96
+ const initialUrl = await react_native_1.Linking.getInitialURL();
97
+ if (initialUrl) {
98
+ if (callback)
99
+ callback(initialUrl);
100
+ return { success: true, url: initialUrl };
101
+ }
102
+ // Generate device fingerprint internally (user doesn't need to know about this)
103
+ const fingerprint = await (0, fingerprint_1.generateDeviceFingerprint)();
104
+ // Fetch deep link URL from API with retry mechanism
105
+ const result = await (0, api_1.fetchDeepLinkUrlWithRetry)(storedLicenseKey, fingerprint, 3, // retry attempts
106
+ DEFAULT_API_ENDPOINT);
107
+ // Call callback if provided and result is successful
108
+ if (callback && result.success && result.url) {
109
+ callback(result.url);
110
+ }
111
+ // If API didn't return a usable URL, return null as per requirements
112
+ if (!result.success || !result.url) {
113
+ return { success: true, url: null, message: 'No deep link available' };
114
+ }
115
+ return result;
116
+ }
117
+ catch (error) {
118
+ console.error('Error in getDeepLink:', error);
119
+ return {
120
+ success: false,
121
+ error: error.message || 'Unknown error occurred',
122
+ };
123
+ }
124
+ }
125
+ /**
126
+ * Check if the package is initialized
127
+ */
128
+ function isReady() {
129
+ return isInitialized && storedLicenseKey !== null;
130
+ }
131
+ /**
132
+ * Reset/clear the stored license key
133
+ * Useful for testing or logout scenarios
134
+ */
135
+ function reset() {
136
+ storedLicenseKey = null;
137
+ isInitialized = false;
138
+ }
139
+ // Keep backward compatibility - export class for advanced users (optional)
140
+ class ProDeepLink {
141
+ constructor(config) {
142
+ const validation = (0, license_1.validateLicenseKeyFormat)(config.licenseKey);
143
+ if (!validation.isValid) {
144
+ throw new Error(validation.message || 'Invalid license key');
145
+ }
146
+ this.licenseKey = config.licenseKey;
147
+ }
148
+ async getDeepLinkUrl() {
149
+ const remoteValidation = await (0, api_1.validateLicenseInit)(this.licenseKey);
150
+ if (!remoteValidation.success) {
151
+ return {
152
+ success: false,
153
+ error: remoteValidation.error || 'License validation failed',
154
+ };
155
+ }
156
+ const fingerprint = await (0, fingerprint_1.generateDeviceFingerprint)();
157
+ return await (0, api_1.fetchDeepLinkUrlWithRetry)(this.licenseKey, fingerprint, 3, DEFAULT_API_ENDPOINT);
158
+ }
159
+ }
160
+ exports.ProDeepLink = ProDeepLink;
161
+ // Export default
162
+ exports.default = { init, getDeepLink, isReady, reset };
@@ -0,0 +1,2 @@
1
+ import { LicenseValidationResult } from './types';
2
+ export declare function validateLicenseKeyFormat(licenseKey: string): LicenseValidationResult;
package/lib/license.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateLicenseKeyFormat = validateLicenseKeyFormat;
4
+ function validateLicenseKeyFormat(licenseKey) {
5
+ if (!licenseKey || typeof licenseKey !== 'string' || !licenseKey.trim()) {
6
+ return {
7
+ isValid: false,
8
+ message: 'License key is required',
9
+ };
10
+ }
11
+ return {
12
+ isValid: true,
13
+ };
14
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ export interface DeviceFingerprint {
2
+ platform: 'ios' | 'android';
3
+ osVersion: string;
4
+ deviceId: string;
5
+ deviceModel: string;
6
+ manufacturer?: string;
7
+ screenResolution: string;
8
+ screenWidth: number;
9
+ screenHeight: number;
10
+ timezone?: string;
11
+ language?: string;
12
+ locale?: string;
13
+ appVersion: string;
14
+ carrier?: string;
15
+ connectionType?: string;
16
+ isSimulator?: boolean;
17
+ isRooted?: boolean;
18
+ ipAddress?: string;
19
+ }
20
+ export interface InitConfig {
21
+ licenseKey: string;
22
+ apiBaseUrl?: string;
23
+ apiPrefix?: string;
24
+ domain?: string;
25
+ }
26
+ export interface DeepLinkConfig {
27
+ licenseKey: string;
28
+ apiEndpoint: string;
29
+ timeout?: number;
30
+ retryAttempts?: number;
31
+ }
32
+ export interface DeepLinkResponse {
33
+ success: boolean;
34
+ url?: string | null;
35
+ message?: string;
36
+ error?: string;
37
+ }
38
+ export interface LicenseValidationResult {
39
+ isValid: boolean;
40
+ message?: string;
41
+ }
42
+ export interface FingerprintBasicPayload {
43
+ userAgent: string;
44
+ language: string;
45
+ platform: string;
46
+ screenResolution: string;
47
+ timezone: string;
48
+ timezoneOffset: number;
49
+ }
50
+ export interface FingerprintNetworkPayload {
51
+ ipAddress: string;
52
+ connectionType: string;
53
+ }
54
+ export interface FingerprintDevicePayload {
55
+ deviceModel: string;
56
+ osVersion: string;
57
+ appVersion: string;
58
+ }
59
+ export interface FingerprintMatchPayload {
60
+ basic: FingerprintBasicPayload;
61
+ network: FingerprintNetworkPayload;
62
+ device: FingerprintDevicePayload;
63
+ userId?: string;
64
+ }
65
+ export interface CustomDeepLinkAnalyticsDeviceInfo {
66
+ userAgent?: string;
67
+ language?: string;
68
+ screenResolution?: string;
69
+ platform?: string;
70
+ [key: string]: any;
71
+ }
72
+ export interface CustomDeepLinkAnalyticsEvent {
73
+ licenseKey: string;
74
+ eventType: string;
75
+ eventName: string;
76
+ category?: string;
77
+ action?: string;
78
+ label?: string;
79
+ value?: number;
80
+ properties?: {
81
+ [key: string]: any;
82
+ };
83
+ sessionId?: string;
84
+ userId?: string;
85
+ pageUrl?: string;
86
+ pageTitle?: string;
87
+ deviceInfo?: CustomDeepLinkAnalyticsDeviceInfo;
88
+ [key: string]: any;
89
+ }
90
+ export interface LicenseFeatures {
91
+ maxLinksPerMonth: number;
92
+ maxDomains: number;
93
+ analyticsLevel: string;
94
+ customBranding: boolean;
95
+ webhookSupport: boolean;
96
+ whiteLabel: boolean;
97
+ apiAccess: boolean;
98
+ rateLimitPerMinute: number;
99
+ [key: string]: any;
100
+ }
101
+ export interface LicenseUsageCurrentMonth {
102
+ linksCreated: number;
103
+ clicks: number;
104
+ apiCalls: number;
105
+ lastReset: string;
106
+ [key: string]: any;
107
+ }
108
+ export interface LicenseUsage {
109
+ totalLinksCreated: number;
110
+ totalClicks: number;
111
+ totalApiCalls: number;
112
+ currentMonth: LicenseUsageCurrentMonth;
113
+ [key: string]: any;
114
+ }
115
+ export interface LicenseData {
116
+ tier: string;
117
+ features: LicenseFeatures;
118
+ validUntil: string;
119
+ usage: LicenseUsage;
120
+ [key: string]: any;
121
+ }
122
+ export interface LicenseValidationApiResponse {
123
+ success: boolean;
124
+ valid?: boolean;
125
+ data?: LicenseData;
126
+ [key: string]: any;
127
+ }
128
+ export interface FingerprintMatchInstallInfo {
129
+ _id: string;
130
+ linkId: string;
131
+ installedAt: string;
132
+ timeToInstall: number;
133
+ [key: string]: any;
134
+ }
135
+ export interface DeepLinkContextMetadata {
136
+ title?: string;
137
+ description?: string;
138
+ imageUrl?: string;
139
+ [key: string]: any;
140
+ }
141
+ export interface DeepLinkContext {
142
+ action: string;
143
+ resourceId?: string;
144
+ params?: {
145
+ [key: string]: any;
146
+ };
147
+ metadata?: DeepLinkContextMetadata;
148
+ campaign?: string;
149
+ source?: string;
150
+ [key: string]: any;
151
+ }
152
+ export interface FingerprintMatchResponse {
153
+ matched: boolean;
154
+ matchConfidence?: number;
155
+ install?: FingerprintMatchInstallInfo;
156
+ deepLinkContext?: DeepLinkContext;
157
+ [key: string]: any;
158
+ }
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "rn-prodeeplinks",
3
+ "version": "0.0.1",
4
+ "description": "Secure deep linking package with license key validation and device fingerprinting for React Native",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build",
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "react-native",
14
+ "deeplink",
15
+ "deep-link",
16
+ "device-fingerprint",
17
+ "license",
18
+ "secure",
19
+ "authentication"
20
+ ],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "peerDependencies": {
24
+ "react-native": ">=0.60.0",
25
+ "react-native-device-info": "^10.0.0",
26
+ "@react-native-community/netinfo": "^9.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/react-native": "^0.72.0",
30
+ "typescript": "^5.0.0"
31
+ },
32
+ "files": [
33
+ "lib",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/2ndGenTech/rn-prodeeplinks.git"
40
+ }
41
+ }