mimz-react-native-tracker 1.0.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 +246 -0
- package/bin/mimz-setup.js +275 -0
- package/package.json +47 -0
- package/src/index.d.ts +131 -0
- package/src/index.js +47 -0
- package/src/network.js +134 -0
- package/src/storage.js +117 -0
- package/src/tracker.js +647 -0
- package/src/utils.js +180 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mimz React Native Tracker SDK
|
|
3
|
+
*
|
|
4
|
+
* Analytics tracking SDK for React Native apps.
|
|
5
|
+
* This is the native equivalent of the Mimz web tracking script.
|
|
6
|
+
*
|
|
7
|
+
* @module mimz-react-native-tracker
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // 1. Install dependencies:
|
|
11
|
+
* // npm install mimz-react-native-tracker @react-native-async-storage/async-storage
|
|
12
|
+
*
|
|
13
|
+
* // 2. Initialize in your App.js or entry file:
|
|
14
|
+
* import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
15
|
+
* import MimzTracker from 'mimz-react-native-tracker';
|
|
16
|
+
*
|
|
17
|
+
* // In your app startup:
|
|
18
|
+
* await MimzTracker.init({
|
|
19
|
+
* clientId: 'YOUR_MIMZ_CLIENT_ID', // From Mimz dashboard
|
|
20
|
+
* backendUrl: 'https://your-mimz-backend.com',
|
|
21
|
+
* asyncStorage: AsyncStorage,
|
|
22
|
+
* appName: 'MyApp',
|
|
23
|
+
* appVersion: '1.0.0',
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // 3. Track deep links (for UTM tracking):
|
|
27
|
+
* MimzTracker.trackDeepLink('myapp://open?utm_source=facebook&utm_campaign=promo');
|
|
28
|
+
*
|
|
29
|
+
* // 4. Track screen views:
|
|
30
|
+
* MimzTracker.trackScreenView('HomeScreen');
|
|
31
|
+
*
|
|
32
|
+
* // 5. Track events:
|
|
33
|
+
* MimzTracker.trackEvent('add_to_cart', { productId: '123', price: 29.99 });
|
|
34
|
+
*
|
|
35
|
+
* // 6. Track conversions:
|
|
36
|
+
* MimzTracker.trackConversion({ productName: 'Premium Plan', value: 49.99 });
|
|
37
|
+
*
|
|
38
|
+
* // 7. Track contacts:
|
|
39
|
+
* MimzTracker.trackContact({ email: 'user@example.com', name: 'John', phone: '+123456' });
|
|
40
|
+
*
|
|
41
|
+
* // 8. Flush on app background:
|
|
42
|
+
* MimzTracker.flush();
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const tracker = require('./tracker');
|
|
46
|
+
|
|
47
|
+
module.exports = tracker;
|
package/src/network.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MimzNetwork - Network layer for sending tracking data to the Mimz backend
|
|
3
|
+
* Replaces browser fetch/sendBeacon with React Native compatible fetch
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
7
|
+
const MAX_RETRY_ATTEMPTS = 2;
|
|
8
|
+
const RETRY_DELAY_MS = 1000;
|
|
9
|
+
|
|
10
|
+
let backendUrl = '';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the network layer with the backend URL
|
|
14
|
+
* @param {string} url - The Mimz backend base URL (e.g., https://mimz-backend.onrender.com)
|
|
15
|
+
*/
|
|
16
|
+
const init = (url) => {
|
|
17
|
+
// Normalize: remove trailing slash
|
|
18
|
+
backendUrl = url.replace(/\/+$/, '');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Send data to the Mimz backend with timeout and retry
|
|
23
|
+
* @param {string} endpoint - API endpoint path (e.g., /api/visitors/add-visitors)
|
|
24
|
+
* @param {object} data - The payload to send
|
|
25
|
+
* @param {object} options - Optional configuration
|
|
26
|
+
* @param {number} options.timeout - Request timeout in ms (default: 5000)
|
|
27
|
+
* @param {number} options.retries - Number of retry attempts (default: 2)
|
|
28
|
+
* @returns {Promise<object|null>} - The response data or null on failure
|
|
29
|
+
*/
|
|
30
|
+
const post = async (endpoint, data, options = {}) => {
|
|
31
|
+
const { timeout = DEFAULT_TIMEOUT_MS, retries = MAX_RETRY_ATTEMPTS } = options;
|
|
32
|
+
|
|
33
|
+
if (!backendUrl) {
|
|
34
|
+
console.warn('[MimzTracker] Backend URL not set. Call MimzTracker.init() first.');
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const url = `${backendUrl}${endpoint}`;
|
|
39
|
+
let lastError = null;
|
|
40
|
+
|
|
41
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
42
|
+
try {
|
|
43
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
44
|
+
const timeoutId = controller ? setTimeout(() => controller.abort(), timeout) : null;
|
|
45
|
+
|
|
46
|
+
const response = await fetch(url, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
'Content-Type': 'application/json',
|
|
50
|
+
Accept: 'application/json',
|
|
51
|
+
'X-Requested-With': 'MimzReactNativeSDK',
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(data),
|
|
54
|
+
signal: controller?.signal,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
58
|
+
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
throw new Error(`Server responded with status ${response.status}: ${response.statusText}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const responseData = await response.json();
|
|
64
|
+
return responseData;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
lastError = error;
|
|
67
|
+
|
|
68
|
+
if (error.name === 'AbortError') {
|
|
69
|
+
console.warn(`[MimzTracker] Request to ${endpoint} timed out (attempt ${attempt + 1}/${retries + 1})`);
|
|
70
|
+
} else {
|
|
71
|
+
console.warn(`[MimzTracker] Request to ${endpoint} failed (attempt ${attempt + 1}/${retries + 1}):`, error.message);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Wait before retrying (skip delay on last attempt)
|
|
75
|
+
if (attempt < retries) {
|
|
76
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS * (attempt + 1)));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.error(`[MimzTracker] All ${retries + 1} attempts to ${endpoint} failed. Last error:`, lastError?.message);
|
|
82
|
+
return null;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get data from the Mimz backend
|
|
87
|
+
* @param {string} endpoint - API endpoint path
|
|
88
|
+
* @param {object} params - Query parameters
|
|
89
|
+
* @returns {Promise<object|null>}
|
|
90
|
+
*/
|
|
91
|
+
const get = async (endpoint, params = {}) => {
|
|
92
|
+
if (!backendUrl) {
|
|
93
|
+
console.warn('[MimzTracker] Backend URL not set.');
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const queryString = Object.entries(params)
|
|
98
|
+
.filter(([, v]) => v != null)
|
|
99
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
100
|
+
.join('&');
|
|
101
|
+
|
|
102
|
+
const url = `${backendUrl}${endpoint}${queryString ? '?' + queryString : ''}`;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
106
|
+
const timeoutId = controller ? setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS) : null;
|
|
107
|
+
|
|
108
|
+
const response = await fetch(url, {
|
|
109
|
+
method: 'GET',
|
|
110
|
+
headers: {
|
|
111
|
+
Accept: 'application/json',
|
|
112
|
+
'X-Requested-With': 'MimzReactNativeSDK',
|
|
113
|
+
},
|
|
114
|
+
signal: controller?.signal,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
throw new Error(`Server responded with status ${response.status}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return await response.json();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.warn(`[MimzTracker] GET ${endpoint} failed:`, error.message);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
init,
|
|
132
|
+
post,
|
|
133
|
+
get,
|
|
134
|
+
};
|
package/src/storage.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MimzStorage - Persistent storage adapter for React Native
|
|
3
|
+
* Replaces browser cookies/localStorage with AsyncStorage
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
let AsyncStorage = null;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the storage adapter with AsyncStorage instance
|
|
10
|
+
* @param {object} asyncStorageInstance - The AsyncStorage module from @react-native-async-storage/async-storage
|
|
11
|
+
*/
|
|
12
|
+
const init = (asyncStorageInstance) => {
|
|
13
|
+
AsyncStorage = asyncStorageInstance;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get a value from persistent storage
|
|
18
|
+
* @param {string} key
|
|
19
|
+
* @returns {Promise<string|null>}
|
|
20
|
+
*/
|
|
21
|
+
const get = async (key) => {
|
|
22
|
+
if (!AsyncStorage) {
|
|
23
|
+
console.warn('[MimzTracker] AsyncStorage not initialized. Call MimzTracker.init() first.');
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const value = await AsyncStorage.getItem(`@mimz_${key}`);
|
|
28
|
+
return value;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.warn(`[MimzTracker] Error reading "${key}" from storage:`, error.message);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set a value in persistent storage
|
|
37
|
+
* @param {string} key
|
|
38
|
+
* @param {string} value
|
|
39
|
+
* @returns {Promise<void>}
|
|
40
|
+
*/
|
|
41
|
+
const set = async (key, value) => {
|
|
42
|
+
if (!AsyncStorage) {
|
|
43
|
+
console.warn('[MimzTracker] AsyncStorage not initialized. Call MimzTracker.init() first.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await AsyncStorage.setItem(`@mimz_${key}`, String(value));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn(`[MimzTracker] Error writing "${key}" to storage:`, error.message);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Remove a value from persistent storage
|
|
55
|
+
* @param {string} key
|
|
56
|
+
* @returns {Promise<void>}
|
|
57
|
+
*/
|
|
58
|
+
const remove = async (key) => {
|
|
59
|
+
if (!AsyncStorage) return;
|
|
60
|
+
try {
|
|
61
|
+
await AsyncStorage.removeItem(`@mimz_${key}`);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.warn(`[MimzTracker] Error removing "${key}" from storage:`, error.message);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get a JSON object from storage
|
|
69
|
+
* @param {string} key
|
|
70
|
+
* @returns {Promise<object|null>}
|
|
71
|
+
*/
|
|
72
|
+
const getJSON = async (key) => {
|
|
73
|
+
const raw = await get(key);
|
|
74
|
+
if (!raw) return null;
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(raw);
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set a JSON object in storage
|
|
84
|
+
* @param {string} key
|
|
85
|
+
* @param {object} value
|
|
86
|
+
* @returns {Promise<void>}
|
|
87
|
+
*/
|
|
88
|
+
const setJSON = async (key, value) => {
|
|
89
|
+
await set(key, JSON.stringify(value));
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Clear all Mimz-related storage keys
|
|
94
|
+
* @returns {Promise<void>}
|
|
95
|
+
*/
|
|
96
|
+
const clearAll = async () => {
|
|
97
|
+
if (!AsyncStorage) return;
|
|
98
|
+
try {
|
|
99
|
+
const allKeys = await AsyncStorage.getAllKeys();
|
|
100
|
+
const mimzKeys = allKeys.filter((key) => key.startsWith('@mimz_'));
|
|
101
|
+
if (mimzKeys.length > 0) {
|
|
102
|
+
await AsyncStorage.multiRemove(mimzKeys);
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.warn('[MimzTracker] Error clearing storage:', error.message);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
init,
|
|
111
|
+
get,
|
|
112
|
+
set,
|
|
113
|
+
remove,
|
|
114
|
+
getJSON,
|
|
115
|
+
setJSON,
|
|
116
|
+
clearAll,
|
|
117
|
+
};
|