mixpanel-react-native 2.4.1 → 3.0.0-beta.2

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.
Files changed (40) hide show
  1. package/.vscode/settings.json +4 -0
  2. package/CHANGELOG.md +84 -51
  3. package/README.md +9 -3
  4. package/Samples/MixpanelExpo/App.js +340 -0
  5. package/Samples/MixpanelExpo/app.json +30 -0
  6. package/Samples/MixpanelExpo/assets/adaptive-icon.png +0 -0
  7. package/Samples/MixpanelExpo/assets/favicon.png +0 -0
  8. package/Samples/MixpanelExpo/assets/icon.png +0 -0
  9. package/Samples/MixpanelExpo/assets/splash.png +0 -0
  10. package/Samples/MixpanelExpo/babel.config.js +6 -0
  11. package/Samples/MixpanelExpo/package-lock.json +22551 -0
  12. package/Samples/MixpanelExpo/package.json +25 -0
  13. package/__mocks__/@react-native-async-storage/async-storage.js +1 -0
  14. package/__tests__/core.test.js +135 -0
  15. package/__tests__/index.test.js +74 -42
  16. package/__tests__/jest_setup.js +23 -4
  17. package/__tests__/main.test.js +788 -0
  18. package/__tests__/network.test.js +72 -0
  19. package/__tests__/queue.test.js +65 -0
  20. package/docs/Mixpanel.html +42 -44
  21. package/docs/MixpanelGroup.html +7 -7
  22. package/docs/People.html +12 -12
  23. package/docs/index.html +5 -4
  24. package/docs/index.js.html +888 -793
  25. package/index.d.ts +75 -53
  26. package/index.js +89 -57
  27. package/javascript/mixpanel-config.js +102 -0
  28. package/javascript/mixpanel-constants.js +22 -0
  29. package/javascript/mixpanel-core.js +163 -0
  30. package/javascript/mixpanel-logger.js +35 -0
  31. package/javascript/mixpanel-main.js +550 -0
  32. package/javascript/mixpanel-network.js +86 -0
  33. package/javascript/mixpanel-persistent.js +312 -0
  34. package/javascript/mixpanel-queue.js +65 -0
  35. package/javascript/mixpanel-storage.js +36 -0
  36. package/javascript/mixpanel-utils.js +38 -0
  37. package/logs/.b11bf985d66a037ca5688a574653f3bf76a28dfa-audit.json +19 -0
  38. package/logs/.c366df74eeb671df60a57a2075ae40a3dae2af25-audit.json +19 -0
  39. package/package.json +11 -6
  40. package/release.py +5 -4
@@ -0,0 +1,102 @@
1
+ import {
2
+ defaultBatchSize,
3
+ defaultFlushInterval,
4
+ defaultServerURL,
5
+ } from "./mixpanel-constants";
6
+
7
+ import {MixpanelLogger} from "./mixpanel-logger";
8
+
9
+ export class MixpanelConfig {
10
+ static instance;
11
+
12
+ static getInstance() {
13
+ if (!MixpanelConfig.instance) {
14
+ MixpanelConfig.instance = new MixpanelConfig();
15
+ }
16
+ return MixpanelConfig.instance;
17
+ }
18
+
19
+ constructor() {
20
+ if (MixpanelConfig.instance) {
21
+ throw new Error(`Use MixpanelConfig.getInstance()`);
22
+ }
23
+ this._config = {};
24
+ }
25
+
26
+ setLoggingEnabled(token, loggingEnabled) {
27
+ this._config[token] = {
28
+ ...this._config[token],
29
+ loggingEnabled,
30
+ };
31
+ if (loggingEnabled) {
32
+ console.info(`Mixpanel Logging Enabled`);
33
+ } else {
34
+ console.info(`Mixpanel Logging Disabled`);
35
+ }
36
+ }
37
+
38
+ getLoggingEnabled(token) {
39
+ return (this._config[token] && this._config[token].loggingEnabled) || false;
40
+ }
41
+
42
+ setServerURL(token, serverURL) {
43
+ this._config[token] = {
44
+ ...this._config[token],
45
+ serverURL,
46
+ };
47
+ MixpanelLogger.log(token, `Set serverURL: ${serverURL}`);
48
+ }
49
+
50
+ getServerURL(token) {
51
+ return (
52
+ (this._config[token] && this._config[token].serverURL) || defaultServerURL
53
+ );
54
+ }
55
+
56
+ setUseIpAddressForGeolocation(token, useIpAddressForGeolocation) {
57
+ this._config[token] = {
58
+ ...this._config[token],
59
+ useIpAddressForGeolocation,
60
+ };
61
+ MixpanelLogger.log(
62
+ token,
63
+ `Set useIpAddressForGeolocation: ${useIpAddressForGeolocation}`
64
+ );
65
+ }
66
+
67
+ getUseIpAddressForGeolocation(token) {
68
+ return (
69
+ (this._config[token] && this._config[token].useIpAddressForGeolocation) ||
70
+ true
71
+ );
72
+ }
73
+
74
+ setFlushBatchSize(token, batchSize) {
75
+ this._config[token] = {
76
+ ...this._config[token],
77
+ batchSize,
78
+ };
79
+ MixpanelLogger.log(token, `Set flush batch size: ${batchSize}`);
80
+ }
81
+
82
+ getFlushBatchSize(token) {
83
+ return (
84
+ (this._config[token] && this._config[token].batchSize) || defaultBatchSize
85
+ );
86
+ }
87
+
88
+ setFlushInterval(token, flushInterval) {
89
+ this._config[token] = {
90
+ ...this._config[token],
91
+ flushInterval,
92
+ };
93
+ MixpanelLogger.log(token, `Set flush interval: ${flushInterval}`);
94
+ }
95
+
96
+ getFlushInterval(token) {
97
+ return (
98
+ (this._config[token] && this._config[token].flushInterval) ||
99
+ defaultFlushInterval
100
+ );
101
+ }
102
+ }
@@ -0,0 +1,22 @@
1
+ export const MixpanelType = {
2
+ EVENTS: "/track/",
3
+ USER: "/engage/",
4
+ GROUPS: "/groups/",
5
+ };
6
+
7
+ export const getQueueKey = (token, type) => `MIXPANEL_${token}_${type}_QUEUE`;
8
+
9
+ export const getDeviceIdKey = (token) => `MIXPANEL_${token}_DEVICE_ID`;
10
+ export const getDistinctIdKey = (token) => `MIXPANEL_${token}_DISTINCT_ID`;
11
+ export const getUserIdKey = (token) => `MIXPANEL_${token}_USER_ID`;
12
+
13
+ export const getOptedOutKey = (token) => `MIXPANEL_${token}_OPT_OUT`;
14
+ export const getSuperPropertiesKey = (token) =>
15
+ `MIXPANEL_${token}_SUPER_PROPERTIES`;
16
+ export const getTimeEventsKey = (token) => `MIXPANEL_${token}_TIME_EVENTS`;
17
+ export const getAppHasOpenedBeforeKey = (token) =>
18
+ `MIXPANEL_${token}_APP_HAS_OPENED_BEFORE`;
19
+
20
+ export const defaultServerURL = `https://api.mixpanel.com`;
21
+ export const defaultBatchSize = 50;
22
+ export const defaultFlushInterval = 10 * 1000; // 10s
@@ -0,0 +1,163 @@
1
+ import {MixpanelQueueManager} from "./mixpanel-queue";
2
+ import {MixpanelNetwork} from "./mixpanel-network";
3
+ import {SessionMetadata} from "./mixpanel-utils";
4
+ import {MixpanelType} from "./mixpanel-constants";
5
+ import {MixpanelConfig} from "./mixpanel-config";
6
+ import {MixpanelPersistent} from "./mixpanel-persistent";
7
+ import {MixpanelLogger} from "./mixpanel-logger";
8
+
9
+ export const MixpanelCore = (storage) => {
10
+ const mixpanelPersistent = MixpanelPersistent.getInstance(storage);
11
+ const config = MixpanelConfig.getInstance();
12
+ let isProcessingQueue = false;
13
+ let processQueueInterval = null;
14
+
15
+ const initialize = async (token) => {
16
+ await MixpanelQueueManager.initialize(token, MixpanelType.EVENTS);
17
+ };
18
+
19
+ const startProcessingQueue = (token) => {
20
+ if (mixpanelPersistent.getOptedOut(token)) {
21
+ MixpanelLogger.log(
22
+ token,
23
+ `User has opted out of tracking, skipping processing queue.`
24
+ );
25
+ return;
26
+ }
27
+
28
+ if (isProcessingQueue) {
29
+ MixpanelLogger.log(
30
+ token,
31
+ `Queue is already being processed. Skipping new cycle.`
32
+ );
33
+ return;
34
+ }
35
+
36
+ isProcessingQueue = true;
37
+
38
+ processQueueInterval = setInterval(async () => {
39
+ clearInterval(processQueueInterval);
40
+ await processQueue(token, MixpanelType.EVENTS);
41
+ await processQueue(token, MixpanelType.USER);
42
+ await processQueue(token, MixpanelType.GROUPS);
43
+
44
+ isProcessingQueue = false;
45
+ startProcessingQueue(token);
46
+ }, config.getFlushInterval(token));
47
+ };
48
+
49
+ const isValidAndSerializable = (token, obj, depth = 1) => {
50
+ try {
51
+ JSON.stringify(obj);
52
+ } catch (error) {
53
+ MixpanelLogger.error(token, `Error in Mixpanel payload: ${error}`);
54
+ return false;
55
+ }
56
+ return true;
57
+ };
58
+
59
+ const addToMixpanelQueue = async (token, type, data) => {
60
+ if (mixpanelPersistent.getOptedOut(token)) {
61
+ MixpanelLogger.log(
62
+ token,
63
+ `User has opted out of tracking, skipping tracking.`
64
+ );
65
+ return;
66
+ }
67
+ if (!isValidAndSerializable(token, data)) {
68
+ MixpanelLogger.error(
69
+ token,
70
+ `The Mixpanel payload is not valid or not serializable.`
71
+ );
72
+ return;
73
+ }
74
+ const sessionMetadata = new SessionMetadata();
75
+ await MixpanelQueueManager.enqueue(token, type, {
76
+ ...sessionMetadata.toDict(type),
77
+ ...data,
78
+ });
79
+ MixpanelLogger.log(
80
+ token,
81
+ `The mixpanel payload is added to the Mixpanel queue. Payload: '${JSON.stringify(
82
+ {
83
+ ...sessionMetadata.toDict(type),
84
+ ...data,
85
+ }
86
+ )}' Type: '${type}' `
87
+ );
88
+ };
89
+
90
+ const flush = async (token) => {
91
+ if (mixpanelPersistent.getOptedOut(token)) {
92
+ MixpanelLogger.log(
93
+ token,
94
+ `User has opted out of tracking, do not flush queue.`
95
+ );
96
+ return;
97
+ }
98
+ await processQueue(token, MixpanelType.EVENTS);
99
+ await processQueue(token, MixpanelType.USER);
100
+ await processQueue(token, MixpanelType.GROUPS);
101
+ };
102
+
103
+ const processQueue = async (token, type) => {
104
+ MixpanelLogger.log(token, `Processing queue for endpoint: ${type}`);
105
+ const processBatch = async () => {
106
+ const queue = MixpanelQueueManager.getQueue(token, type);
107
+ if (queue.length > 0) {
108
+ MixpanelLogger.log(token, `[Flushing queue] endpoint: ${type}`);
109
+ MixpanelLogger.log(
110
+ token,
111
+ `[Flushing queue] queue: ${JSON.stringify(queue)}`
112
+ );
113
+ const batchSize = config.getFlushBatchSize(token);
114
+ const batch = queue.slice(0, batchSize);
115
+ try {
116
+ await MixpanelNetwork.sendRequest({
117
+ token,
118
+ data: batch,
119
+ endpoint: type,
120
+ serverURL: config.getServerURL(token),
121
+ useIPAddressForGeoLocation:
122
+ config.getUseIpAddressForGeolocation(token),
123
+ });
124
+ await MixpanelQueueManager.spliceQueue(token, type, 0, batch.length);
125
+ // Process the next batch if there are more events in the queue
126
+ const queue = MixpanelQueueManager.getQueue(token, type);
127
+ if (queue.length > 0) {
128
+ setTimeout(processBatch, 0);
129
+ }
130
+ } catch (error) {
131
+ handleBatchError(token, error, type, processBatch);
132
+ }
133
+ }
134
+ };
135
+
136
+ processBatch();
137
+ };
138
+
139
+ const handleBatchError = (token, error, type, callback) => {
140
+ if (error.code === 400) {
141
+ MixpanelLogger.error(
142
+ token,
143
+ `Bad request received due to corrupted data within the batch. The corrupted data is now being removed from the queue...`
144
+ );
145
+ // Remove the corrupted data from the queue, to avoid the data loss, only remove one event at a time
146
+ MixpanelQueueManager.spliceQueue(token, type, 0, 1).then(() => {
147
+ setTimeout(callback, 0);
148
+ });
149
+ } else {
150
+ MixpanelLogger.error(
151
+ token,
152
+ `Error sending event batch from queue, error: ${error}`
153
+ );
154
+ }
155
+ };
156
+
157
+ return {
158
+ initialize,
159
+ startProcessingQueue,
160
+ addToMixpanelQueue,
161
+ flush,
162
+ };
163
+ };
@@ -0,0 +1,35 @@
1
+ import {MixpanelConfig} from "mixpanel-react-native/javascript/mixpanel-config";
2
+
3
+ export class MixpanelLogger {
4
+ static _shouldLog(token) {
5
+ return MixpanelConfig.getInstance().getLoggingEnabled(token);
6
+ }
7
+
8
+ static _prependPrefix(args) {
9
+ return ["[Mixpanel]", ...args];
10
+ }
11
+
12
+ static log(token, ...args) {
13
+ if (MixpanelLogger._shouldLog(token)) {
14
+ console.log(...MixpanelLogger._prependPrefix(args));
15
+ }
16
+ }
17
+
18
+ static info(token, ...args) {
19
+ if (MixpanelLogger._shouldLog(token)) {
20
+ console.info(...MixpanelLogger._prependPrefix(args));
21
+ }
22
+ }
23
+
24
+ static warn(token, ...args) {
25
+ if (MixpanelLogger._shouldLog(token)) {
26
+ console.warn(...MixpanelLogger._prependPrefix(args));
27
+ }
28
+ }
29
+
30
+ static error(token, ...args) {
31
+ if (MixpanelLogger._shouldLog(token)) {
32
+ console.error(...MixpanelLogger._prependPrefix(args));
33
+ }
34
+ }
35
+ }