mixpanel-react-native 3.2.0-beta.1 → 3.2.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.
@@ -0,0 +1,348 @@
1
+ # Feature Flags Quick Start Guide (Beta)
2
+
3
+ > **Beta Version:** `3.2.0-beta.1`
4
+ > **Native Mode Only:** This beta release supports iOS and Android native implementations. JavaScript mode (Expo/React Native Web) support coming in future release.
5
+
6
+ ## Installation
7
+
8
+ Install the beta version:
9
+
10
+ ```bash
11
+ npm install mixpanel-react-native@3.2.0-beta.1
12
+ ```
13
+
14
+ For iOS, update native dependencies:
15
+
16
+ ```bash
17
+ cd ios && pod install
18
+ ```
19
+
20
+ ## Basic Setup
21
+
22
+ ### 1. Initialize with Feature Flags Enabled
23
+
24
+ ```javascript
25
+ import { Mixpanel } from 'mixpanel-react-native';
26
+
27
+ const mixpanel = new Mixpanel('YOUR_TOKEN');
28
+
29
+ // Enable Feature Flags during initialization
30
+ await mixpanel.init(
31
+ false, // optOutTrackingDefault
32
+ {}, // superProperties
33
+ 'https://api.mixpanel.com', // serverURL
34
+ true, // useGzipCompression
35
+ {
36
+ enabled: true, // Enable Feature Flags
37
+ context: { // Optional: Add targeting context
38
+ platform: 'mobile',
39
+ app_version: '2.1.0'
40
+ }
41
+ }
42
+ );
43
+ ```
44
+
45
+ ### 2. Check Flag Availability
46
+
47
+ Before accessing flags, verify they're loaded:
48
+
49
+ ```javascript
50
+ if (mixpanel.flags.areFlagsReady()) {
51
+ // Flags are ready to use
52
+ console.log('Feature flags loaded!');
53
+ }
54
+ ```
55
+
56
+ ## Using Feature Flags
57
+
58
+ ### Synchronous API (Recommended for UI)
59
+
60
+ Use sync methods when flags are ready (e.g., in render methods):
61
+
62
+ ```javascript
63
+ // Check if feature is enabled
64
+ const showNewUI = mixpanel.flags.isEnabledSync('new-checkout-flow', false);
65
+
66
+ // Get variant value directly
67
+ const buttonColor = mixpanel.flags.getVariantValueSync('button-color', 'blue');
68
+
69
+ // Get full variant object with metadata
70
+ const variant = mixpanel.flags.getVariantSync('pricing-tier', {
71
+ key: 'control',
72
+ value: 'standard'
73
+ });
74
+
75
+ console.log(`Variant: ${variant.key}, Value: ${variant.value}`);
76
+ if (variant.experiment_id) {
77
+ console.log(`Part of experiment: ${variant.experiment_id}`);
78
+ }
79
+ ```
80
+
81
+ ### Asynchronous API (Promise Pattern)
82
+
83
+ Use async methods for event handlers or initialization:
84
+
85
+ ```javascript
86
+ // Promise pattern
87
+ const variant = await mixpanel.flags.getVariant('checkout-flow', {
88
+ key: 'control',
89
+ value: 'standard'
90
+ });
91
+
92
+ const enabled = await mixpanel.flags.isEnabled('dark-mode', false);
93
+
94
+ const colorValue = await mixpanel.flags.getVariantValue('theme-color', '#0000FF');
95
+ ```
96
+
97
+ ### Asynchronous API (Callback Pattern)
98
+
99
+ Alternative callback style for compatibility:
100
+
101
+ ```javascript
102
+ // Callback pattern
103
+ mixpanel.flags.getVariant('feature-name', { key: 'control', value: 'off' }, (variant) => {
104
+ console.log(`Feature variant: ${variant.key}`);
105
+ });
106
+
107
+ mixpanel.flags.isEnabled('new-feature', false, (isEnabled) => {
108
+ if (isEnabled) {
109
+ // Show new feature
110
+ }
111
+ });
112
+ ```
113
+
114
+ ## Real-World Examples
115
+
116
+ ### Example 1: Feature Toggle
117
+
118
+ ```javascript
119
+ const NewCheckoutButton = () => {
120
+ const [showNewCheckout, setShowNewCheckout] = useState(false);
121
+
122
+ useEffect(() => {
123
+ // Load flags on mount
124
+ if (mixpanel.flags.areFlagsReady()) {
125
+ const enabled = mixpanel.flags.isEnabledSync('new-checkout', false);
126
+ setShowNewCheckout(enabled);
127
+ }
128
+ }, []);
129
+
130
+ return showNewCheckout ? <NewCheckout /> : <LegacyCheckout />;
131
+ };
132
+ ```
133
+
134
+ ### Example 2: A/B Test with Variants
135
+
136
+ ```javascript
137
+ const ProductCard = ({ product }) => {
138
+ // Get button color variant (A/B test)
139
+ const buttonColor = mixpanel.flags.areFlagsReady()
140
+ ? mixpanel.flags.getVariantValueSync('button-color', 'blue')
141
+ : 'blue';
142
+
143
+ // Get pricing display variant
144
+ const pricingVariant = mixpanel.flags.areFlagsReady()
145
+ ? mixpanel.flags.getVariantSync('pricing-display', {
146
+ key: 'control',
147
+ value: 'standard'
148
+ })
149
+ : { key: 'control', value: 'standard' };
150
+
151
+ return (
152
+ <View>
153
+ <Text>{product.name}</Text>
154
+ {pricingVariant.value === 'bold' ? (
155
+ <Text style={styles.boldPrice}>${product.price}</Text>
156
+ ) : (
157
+ <Text>${product.price}</Text>
158
+ )}
159
+ <Button
160
+ title="Add to Cart"
161
+ color={buttonColor}
162
+ onPress={handleAddToCart}
163
+ />
164
+ </View>
165
+ );
166
+ };
167
+ ```
168
+
169
+ ### Example 3: Gradual Rollout with Fallback
170
+
171
+ ```javascript
172
+ const App = () => {
173
+ const [uiVersion, setUiVersion] = useState('v1');
174
+
175
+ useEffect(() => {
176
+ const loadFlags = async () => {
177
+ try {
178
+ // Manually trigger flag load
179
+ await mixpanel.flags.loadFlags();
180
+
181
+ // Check which UI version to show
182
+ const variant = mixpanel.flags.getVariantSync('ui-redesign', {
183
+ key: 'control',
184
+ value: 'v1'
185
+ });
186
+
187
+ setUiVersion(variant.value);
188
+
189
+ // Track if user is in experiment
190
+ if (variant.experiment_id) {
191
+ console.log(`User in experiment: ${variant.experiment_id}`);
192
+ }
193
+ } catch (error) {
194
+ console.error('Failed to load flags:', error);
195
+ // Fallback to default
196
+ }
197
+ };
198
+
199
+ loadFlags();
200
+ }, []);
201
+
202
+ return uiVersion === 'v2' ? <NewUI /> : <LegacyUI />;
203
+ };
204
+ ```
205
+
206
+ ### Example 4: Targeting with Context
207
+
208
+ ```javascript
209
+ // Set user context for targeting
210
+ await mixpanel.init(
211
+ false,
212
+ {},
213
+ 'https://api.mixpanel.com',
214
+ true,
215
+ {
216
+ enabled: true,
217
+ context: {
218
+ user_tier: 'premium',
219
+ device_type: Platform.OS,
220
+ app_version: '2.1.0',
221
+ custom_properties: {
222
+ beta_tester: true,
223
+ region: 'US'
224
+ }
225
+ }
226
+ }
227
+ );
228
+
229
+ // Flags will be evaluated based on context
230
+ const hasAccess = mixpanel.flags.isEnabledSync('premium-feature', false);
231
+ ```
232
+
233
+ ## API Reference
234
+
235
+ ### Methods
236
+
237
+ | Method | Type | Description |
238
+ |--------|------|-------------|
239
+ | `areFlagsReady()` | Sync | Returns `true` if flags are loaded |
240
+ | `loadFlags()` | Async | Manually fetch flags from server |
241
+ | `isEnabledSync(name, fallback)` | Sync | Check if feature is enabled |
242
+ | `isEnabled(name, fallback)` | Async | Async version of isEnabledSync |
243
+ | `getVariantValueSync(name, fallback)` | Sync | Get variant value only |
244
+ | `getVariantValue(name, fallback)` | Async | Async version of getVariantValueSync |
245
+ | `getVariantSync(name, fallback)` | Sync | Get full variant object |
246
+ | `getVariant(name, fallback)` | Async | Async version of getVariantSync |
247
+
248
+ ### Snake Case Aliases
249
+
250
+ All methods have snake_case aliases for consistency with mixpanel-js:
251
+
252
+ ```javascript
253
+ // These are equivalent
254
+ mixpanel.flags.areFlagsReady()
255
+ mixpanel.flags.are_flags_ready()
256
+
257
+ mixpanel.flags.getVariantSync('feature', fallback)
258
+ mixpanel.flags.get_variant_sync('feature', fallback)
259
+ ```
260
+
261
+ ## Automatic Experiment Tracking
262
+
263
+ When a user is evaluated for a flag that's part of an A/B test, Mixpanel automatically tracks an `$experiment_started` event. No additional code required!
264
+
265
+ ## Important Notes
266
+
267
+ ### Platform Support
268
+
269
+ - ✅ **iOS**: Full support via native Swift SDK
270
+ - ✅ **Android**: Full support via native Android SDK
271
+ - ❌ **Expo/React Native Web**: Not supported in this beta (coming soon)
272
+
273
+ ### Fallback Values
274
+
275
+ Always provide fallback values for graceful degradation:
276
+
277
+ ```javascript
278
+ // Good - provides fallback
279
+ const color = mixpanel.flags.getVariantValueSync('color', 'blue');
280
+
281
+ // Bad - could return undefined if flag not found
282
+ const color = mixpanel.flags.getVariantValueSync('color');
283
+ ```
284
+
285
+ ### Performance
286
+
287
+ - Flags are lazy-loaded on first access
288
+ - Sync methods are preferred for UI rendering (no async overhead)
289
+ - Check `areFlagsReady()` before using sync methods
290
+
291
+ ### Error Handling
292
+
293
+ Flags fail gracefully and return fallback values:
294
+
295
+ ```javascript
296
+ try {
297
+ await mixpanel.flags.loadFlags();
298
+ } catch (error) {
299
+ console.error('Flag loading failed:', error);
300
+ // App continues with fallback values
301
+ }
302
+ ```
303
+
304
+ ## Troubleshooting
305
+
306
+ ### Flags Not Loading
307
+
308
+ 1. Verify Feature Flags are enabled in your Mixpanel project
309
+ 2. Check initialization includes `featureFlagsOptions: { enabled: true }`
310
+ 3. Enable logging: `mixpanel.setLoggingEnabled(true)`
311
+ 4. Verify network connectivity to Mixpanel API
312
+
313
+ ### Native Module Not Found
314
+
315
+ This beta requires native modules. If you see "Native module not found":
316
+
317
+ 1. Run `cd ios && pod install` (iOS)
318
+ 2. Rebuild the app completely
319
+ 3. Verify you're not using Expo Go (use dev client or eject)
320
+
321
+ ### Getting Fallback Values
322
+
323
+ If flags always return fallbacks:
324
+
325
+ 1. Check `mixpanel.flags.areFlagsReady()` returns `true`
326
+ 2. Verify flag names match exactly (case-sensitive)
327
+ 3. Confirm flags are published in Mixpanel dashboard
328
+ 4. Check context targeting rules
329
+
330
+ ## Feedback & Issues
331
+
332
+ This is a beta release. Please report issues at:
333
+ https://github.com/mixpanel/mixpanel-react-native/issues
334
+
335
+ Reference PR #331 for technical details:
336
+ https://github.com/mixpanel/mixpanel-react-native/pull/331
337
+
338
+ ## What's Next
339
+
340
+ Coming in future releases:
341
+
342
+ - JavaScript mode support (Expo/React Native Web)
343
+ - Runtime context updates via `updateContext()`
344
+ - Performance optimizations
345
+
346
+ ---
347
+
348
+ **Questions?** Visit [Mixpanel Documentation](https://mixpanel.com) or reach out to support.
package/README.md CHANGED
@@ -115,6 +115,35 @@ const SampleApp = () => {
115
115
  export default SampleApp;
116
116
  ```
117
117
 
118
+ ### Feature Flags (Beta - 3.2.0-beta.1+)
119
+
120
+ Control features dynamically and run A/B tests with Mixpanel Feature Flags. **Native mode only** (iOS/Android) in beta.
121
+
122
+ #### Quick Start
123
+
124
+ ```js
125
+ // Enable during initialization
126
+ await mixpanel.init(false, {}, 'https://api.mixpanel.com', true, {
127
+ enabled: true,
128
+ context: { platform: 'mobile' } // Optional targeting context
129
+ });
130
+
131
+ // Check if feature is enabled
132
+ if (mixpanel.flags.areFlagsReady()) {
133
+ const showNewUI = mixpanel.flags.isEnabledSync('new-feature', false);
134
+ const buttonColor = mixpanel.flags.getVariantValueSync('button-color', 'blue');
135
+ }
136
+ ```
137
+
138
+ #### Key Methods
139
+
140
+ - `areFlagsReady()` - Check if flags are loaded
141
+ - `isEnabledSync(name, fallback)` - Check if feature is enabled
142
+ - `getVariantValueSync(name, fallback)` - Get variant value
143
+ - `getVariantSync(name, fallback)` - Get full variant object with metadata
144
+
145
+ All methods support async versions and snake_case aliases. See **[Feature Flags Quick Start Guide](FEATURE_FLAGS_QUICKSTART.md)** for complete documentation.
146
+
118
147
  ### Expo and React Native for Web support (3.0.2 and above)
119
148
 
120
149
  Starting from version 3.0.2, we have introduced support for Expo, React Native for Web, and other platforms utilizing React Native that do not support iOS and Android directly.
package/index.js CHANGED
@@ -63,10 +63,27 @@ export class Mixpanel {
63
63
 
64
64
  /**
65
65
  * Returns the Flags instance for feature flags operations.
66
- * This property is lazy-loaded to avoid unnecessary initialization.
67
66
  *
68
- * NOTE: Feature flags are only available in native mode.
69
- * JavaScript mode is not yet supported.
67
+ * <p>Feature Flags enable dynamic feature control and A/B testing capabilities.
68
+ * This property is lazy-loaded to avoid unnecessary initialization until first access.
69
+ *
70
+ * <p><b>Native Mode Only:</b> Feature flags are currently only available when using native mode
71
+ * (iOS/Android). JavaScript mode (Expo/React Native Web) support is planned for a future release.
72
+ *
73
+ * @return {Flags} an instance of Flags that provides access to feature flag operations
74
+ * @throws {Error} if accessed in JavaScript mode (when native modules are not available)
75
+ *
76
+ * @example
77
+ * // Check if flags are ready
78
+ * if (mixpanel.flags.areFlagsReady()) {
79
+ * const isEnabled = mixpanel.flags.isEnabledSync('new-checkout', false);
80
+ * }
81
+ *
82
+ * @example
83
+ * // Get a feature variant value
84
+ * const buttonColor = mixpanel.flags.getVariantValueSync('button-color', 'blue');
85
+ *
86
+ * @see Flags
70
87
  */
71
88
  get flags() {
72
89
  // Short circuit for JavaScript mode - flags not ready for public use
@@ -86,13 +103,50 @@ export class Mixpanel {
86
103
  }
87
104
 
88
105
  /**
89
- * Initializes Mixpanel
90
- *
91
- * @param {boolean} optOutTrackingDefault Optional Whether or not Mixpanel can start tracking by default. See optOutTracking()
92
- * @param {object} superProperties Optional A Map containing the key value pairs of the super properties to register
93
- * @param {string} serverURL Optional Set the base URL used for Mixpanel API requests. See setServerURL()
94
- * @param {boolean} useGzipCompression Optional Set whether to use gzip compression for network requests. Defaults to false.
95
- * @param {object} featureFlagsOptions Optional Feature flags configuration including enabled flag and context
106
+ * Initializes Mixpanel with optional configuration for tracking, super properties, and feature flags.
107
+ *
108
+ * <p>This method must be called before using any other Mixpanel functionality. It sets up
109
+ * the tracking environment, registers super properties, and optionally initializes feature flags.
110
+ *
111
+ * @param {boolean} [optOutTrackingDefault=false] Whether or not Mixpanel can start tracking by default.
112
+ * If true, no data will be tracked until optInTracking() is called. See optOutTracking()
113
+ * @param {object} [superProperties={}] A Map containing the key value pairs of the super properties to register.
114
+ * These properties will be sent with every event. Pass {} if no super properties needed.
115
+ * @param {string} [serverURL="https://api.mixpanel.com"] The base URL used for Mixpanel API requests.
116
+ * Use "https://api-eu.mixpanel.com" for EU data residency. See setServerURL()
117
+ * @param {boolean} [useGzipCompression=false] Whether to use gzip compression for network requests.
118
+ * Enabling this reduces bandwidth usage but adds slight CPU overhead.
119
+ * @param {object} [featureFlagsOptions={}] Feature flags configuration object with the following properties:
120
+ * @param {boolean} [featureFlagsOptions.enabled=false] Whether to enable feature flags functionality
121
+ * @param {object} [featureFlagsOptions.context={}] Context properties used for feature flag targeting.
122
+ * Can include user properties, device properties, or any custom properties for flag evaluation.
123
+ * Note: In native mode, context must be set during initialization and cannot be updated later.
124
+ * @returns {Promise<void>} A promise that resolves when initialization is complete
125
+ *
126
+ * @example
127
+ * // Basic initialization
128
+ * const mixpanel = new Mixpanel('YOUR_TOKEN', true);
129
+ * await mixpanel.init();
130
+ *
131
+ * @example
132
+ * // Initialize with feature flags enabled
133
+ * const mixpanel = new Mixpanel('YOUR_TOKEN', true);
134
+ * await mixpanel.init(false, {}, 'https://api.mixpanel.com', false, {
135
+ * enabled: true,
136
+ * context: {
137
+ * platform: 'mobile',
138
+ * app_version: '2.1.0'
139
+ * }
140
+ * });
141
+ *
142
+ * @example
143
+ * // Initialize with EU data residency and super properties
144
+ * await mixpanel.init(
145
+ * false,
146
+ * { plan: 'premium', region: 'eu' },
147
+ * 'https://api-eu.mixpanel.com',
148
+ * true
149
+ * );
96
150
  */
97
151
  async init(
98
152
  optOutTrackingDefault = DEFAULT_OPT_OUT,
@@ -1,8 +1,68 @@
1
1
  import { MixpanelFlagsJS } from './mixpanel-flags-js';
2
2
 
3
3
  /**
4
- * Flags class for managing Feature Flags functionality
5
- * This class handles both native and JavaScript fallback implementations
4
+ * Core class for using Mixpanel Feature Flags.
5
+ *
6
+ * <p>The Flags class provides access to Mixpanel's Feature Flags functionality, enabling
7
+ * dynamic feature control, A/B testing, and personalized user experiences. Feature flags
8
+ * allow you to remotely configure your app's features without deploying new code.
9
+ *
10
+ * <p>This class is accessed through the {@link Mixpanel#flags} property and is lazy-loaded
11
+ * to minimize performance impact until feature flags are actually used.
12
+ *
13
+ * <p><b>Platform Support:</b>
14
+ * <ul>
15
+ * <li><b>Native Mode (iOS/Android):</b> Fully supported with automatic experiment tracking</li>
16
+ * <li><b>JavaScript Mode (Expo/React Native Web):</b> Planned for future release</li>
17
+ * </ul>
18
+ *
19
+ * <p><b>Key Concepts:</b>
20
+ * <ul>
21
+ * <li><b>Feature Name:</b> The unique identifier for your feature flag (e.g., "new-checkout")</li>
22
+ * <li><b>Variant:</b> An object containing both a key and value representing the feature configuration</li>
23
+ * <li><b>Variant Key:</b> The identifier for the specific variation (e.g., "control", "treatment")</li>
24
+ * <li><b>Variant Value:</b> The actual configuration value (can be any JSON-serializable type)</li>
25
+ * <li><b>Fallback:</b> Default value returned when a flag is not available or not loaded</li>
26
+ * </ul>
27
+ *
28
+ * <p><b>Automatic Experiment Tracking:</b> When a feature flag is evaluated for the first time,
29
+ * Mixpanel automatically tracks a "$experiment_started" event with relevant metadata.
30
+ *
31
+ * @example
32
+ * // Initialize with feature flags enabled
33
+ * const mixpanel = new Mixpanel('YOUR_TOKEN', true);
34
+ * await mixpanel.init(false, {}, 'https://api.mixpanel.com', false, {
35
+ * enabled: true,
36
+ * context: { platform: 'mobile' }
37
+ * });
38
+ *
39
+ * @example
40
+ * // Synchronous access (when flags are ready)
41
+ * if (mixpanel.flags.areFlagsReady()) {
42
+ * const isEnabled = mixpanel.flags.isEnabledSync('new-feature', false);
43
+ * const color = mixpanel.flags.getVariantValueSync('button-color', 'blue');
44
+ * const variant = mixpanel.flags.getVariantSync('checkout-flow', {
45
+ * key: 'control',
46
+ * value: 'standard'
47
+ * });
48
+ * }
49
+ *
50
+ * @example
51
+ * // Asynchronous access with Promise pattern
52
+ * const variant = await mixpanel.flags.getVariant('pricing-test', {
53
+ * key: 'control',
54
+ * value: { price: 9.99, currency: 'USD' }
55
+ * });
56
+ *
57
+ * @example
58
+ * // Asynchronous access with callback pattern
59
+ * mixpanel.flags.isEnabled('beta-features', false, (isEnabled) => {
60
+ * if (isEnabled) {
61
+ * // Enable beta features
62
+ * }
63
+ * });
64
+ *
65
+ * @see Mixpanel#flags
6
66
  */
7
67
  export class Flags {
8
68
  constructor(token, mixpanelImpl, storage) {
@@ -18,8 +78,26 @@ export class Flags {
18
78
  }
19
79
 
20
80
  /**
21
- * Manually trigger a fetch of feature flags from the Mixpanel servers.
22
- * This is usually automatic but can be called manually if needed.
81
+ * Manually fetch feature flags from the Mixpanel servers.
82
+ *
83
+ * <p>Feature flags are automatically loaded during initialization when feature flags are enabled.
84
+ * This method allows you to manually trigger a refresh of the flags, which is useful when:
85
+ * <ul>
86
+ * <li>You want to reload flags after a user property change</li>
87
+ * <li>You need to ensure you have the latest flag configuration</li>
88
+ * <li>Initial automatic load failed and you want to retry</li>
89
+ * </ul>
90
+ *
91
+ * <p>After successfully loading flags, {@link areFlagsReady} will return true and synchronous
92
+ * methods can be used to access flag values.
93
+ *
94
+ * @returns {Promise<void>} A promise that resolves when flags have been fetched and loaded
95
+ * @throws {Error} if feature flags are not initialized
96
+ *
97
+ * @example
98
+ * // Manually reload flags after user identification
99
+ * await mixpanel.identify('user123');
100
+ * await mixpanel.flags.loadFlags();
23
101
  */
24
102
  async loadFlags() {
25
103
  if (this.isNativeMode) {
@@ -31,8 +109,31 @@ export class Flags {
31
109
  }
32
110
 
33
111
  /**
34
- * Check if feature flags have been fetched and are ready to use.
35
- * @returns {boolean} True if flags are ready, false otherwise
112
+ * Check if feature flags have been fetched from the server and are ready to use.
113
+ *
114
+ * <p>This method returns true after feature flags have been successfully loaded via {@link loadFlags}
115
+ * or during initialization. When flags are ready, you can safely use the synchronous methods
116
+ * ({@link getVariantSync}, {@link getVariantValueSync}, {@link isEnabledSync}) without waiting.
117
+ *
118
+ * <p>It's recommended to check this before using synchronous methods to ensure you're not
119
+ * getting fallback values due to flags not being loaded yet.
120
+ *
121
+ * @returns {boolean} true if flags have been loaded and are ready to use, false otherwise
122
+ *
123
+ * @example
124
+ * // Check before using synchronous methods
125
+ * if (mixpanel.flags.areFlagsReady()) {
126
+ * const isEnabled = mixpanel.flags.isEnabledSync('new-feature', false);
127
+ * } else {
128
+ * console.log('Flags not ready yet, using fallback');
129
+ * }
130
+ *
131
+ * @example
132
+ * // Wait for flags to be ready
133
+ * await mixpanel.flags.loadFlags();
134
+ * if (mixpanel.flags.areFlagsReady()) {
135
+ * // Now safe to use sync methods
136
+ * }
36
137
  */
37
138
  areFlagsReady() {
38
139
  if (this.isNativeMode) {
@@ -44,10 +145,51 @@ export class Flags {
44
145
  }
45
146
 
46
147
  /**
47
- * Get a feature flag variant synchronously. Only works when flags are ready.
48
- * @param {string} featureName - Name of the feature flag
49
- * @param {object} fallback - Fallback variant if flag is not available
50
- * @returns {object} The flag variant with key and value properties
148
+ * Get a feature flag variant synchronously.
149
+ *
150
+ * <p>Returns the complete variant object for a feature flag, including both the variant key
151
+ * (e.g., "control", "treatment") and the variant value (the actual configuration data).
152
+ *
153
+ * <p><b>Important:</b> This is a synchronous method that only works when flags are ready.
154
+ * Always check {@link areFlagsReady} first, or use the asynchronous {@link getVariant} method instead.
155
+ *
156
+ * <p>When a flag is evaluated for the first time, Mixpanel automatically tracks a
157
+ * "$experiment_started" event with relevant experiment metadata.
158
+ *
159
+ * @param {string} featureName The unique identifier for the feature flag
160
+ * @param {object} fallback The fallback variant object to return if the flag is not available.
161
+ * Must include both 'key' and 'value' properties.
162
+ * @returns {object} The flag variant object with the following structure:
163
+ * - key: {string} The variant key (e.g., "control", "treatment")
164
+ * - value: {any} The variant value (can be any JSON-serializable type)
165
+ * - experiment_id: {string|number} (optional) The experiment ID if this is an experiment
166
+ * - is_experiment_active: {boolean} (optional) Whether the experiment is currently active
167
+ *
168
+ * @example
169
+ * // Get a checkout flow variant
170
+ * if (mixpanel.flags.areFlagsReady()) {
171
+ * const variant = mixpanel.flags.getVariantSync('checkout-flow', {
172
+ * key: 'control',
173
+ * value: 'standard'
174
+ * });
175
+ * console.log(`Using variant: ${variant.key}`);
176
+ * console.log(`Configuration: ${JSON.stringify(variant.value)}`);
177
+ * }
178
+ *
179
+ * @example
180
+ * // Get a complex configuration variant
181
+ * const defaultConfig = {
182
+ * key: 'default',
183
+ * value: {
184
+ * theme: 'light',
185
+ * layout: 'grid',
186
+ * itemsPerPage: 20
187
+ * }
188
+ * };
189
+ * const config = mixpanel.flags.getVariantSync('ui-config', defaultConfig);
190
+ *
191
+ * @see getVariant for asynchronous access
192
+ * @see getVariantValueSync to get only the value (not the full variant object)
51
193
  */
52
194
  getVariantSync(featureName, fallback) {
53
195
  if (!this.areFlagsReady()) {
@@ -63,10 +205,45 @@ export class Flags {
63
205
  }
64
206
 
65
207
  /**
66
- * Get a feature flag variant value synchronously. Only works when flags are ready.
67
- * @param {string} featureName - Name of the feature flag
68
- * @param {any} fallbackValue - Fallback value if flag is not available
69
- * @returns {any} The flag value
208
+ * Get a feature flag variant value synchronously.
209
+ *
210
+ * <p>Returns only the value portion of a feature flag variant, without the variant key or metadata.
211
+ * This is useful when you only care about the configuration data, not which variant was selected.
212
+ *
213
+ * <p><b>Important:</b> This is a synchronous method that only works when flags are ready.
214
+ * Always check {@link areFlagsReady} first, or use the asynchronous {@link getVariantValue} method instead.
215
+ *
216
+ * <p>When a flag is evaluated for the first time, Mixpanel automatically tracks a
217
+ * "$experiment_started" event with relevant experiment metadata.
218
+ *
219
+ * @param {string} featureName The unique identifier for the feature flag
220
+ * @param {any} fallbackValue The fallback value to return if the flag is not available.
221
+ * Can be any JSON-serializable type (string, number, boolean, object, array, etc.)
222
+ * @returns {any} The flag's value, or the fallback if the flag is not available.
223
+ * The return type matches the type of value configured in your Mixpanel project.
224
+ *
225
+ * @example
226
+ * // Get a simple string value
227
+ * if (mixpanel.flags.areFlagsReady()) {
228
+ * const buttonColor = mixpanel.flags.getVariantValueSync('button-color', 'blue');
229
+ * applyButtonColor(buttonColor);
230
+ * }
231
+ *
232
+ * @example
233
+ * // Get a complex object value
234
+ * const defaultPricing = { price: 9.99, currency: 'USD', trial_days: 7 };
235
+ * const pricing = mixpanel.flags.getVariantValueSync('pricing-config', defaultPricing);
236
+ * console.log(`Price: ${pricing.price} ${pricing.currency}`);
237
+ *
238
+ * @example
239
+ * // Get a boolean value
240
+ * const showPromo = mixpanel.flags.getVariantValueSync('show-promo', false);
241
+ * if (showPromo) {
242
+ * displayPromotionalBanner();
243
+ * }
244
+ *
245
+ * @see getVariantValue for asynchronous access
246
+ * @see getVariantSync to get the full variant object including key and metadata
70
247
  */
71
248
  getVariantValueSync(featureName, fallbackValue) {
72
249
  if (!this.areFlagsReady()) {
@@ -89,10 +266,39 @@ export class Flags {
89
266
  }
90
267
 
91
268
  /**
92
- * Check if a feature flag is enabled synchronously. Only works when flags are ready.
93
- * @param {string} featureName - Name of the feature flag
94
- * @param {boolean} fallbackValue - Fallback value if flag is not available
95
- * @returns {boolean} True if enabled, false otherwise
269
+ * Check if a feature flag is enabled synchronously.
270
+ *
271
+ * <p>This is a convenience method for boolean feature flags. It checks if a feature is enabled
272
+ * by evaluating the variant value as a boolean. A feature is considered "enabled" when its
273
+ * variant value evaluates to true.
274
+ *
275
+ * <p><b>Important:</b> This is a synchronous method that only works when flags are ready.
276
+ * Always check {@link areFlagsReady} first, or use the asynchronous {@link isEnabled} method instead.
277
+ *
278
+ * <p>When a flag is evaluated for the first time, Mixpanel automatically tracks a
279
+ * "$experiment_started" event with relevant experiment metadata.
280
+ *
281
+ * @param {string} featureName The unique identifier for the feature flag
282
+ * @param {boolean} [fallbackValue=false] The fallback value to return if the flag is not available.
283
+ * Defaults to false if not provided.
284
+ * @returns {boolean} true if the feature is enabled, false otherwise
285
+ *
286
+ * @example
287
+ * // Simple feature toggle
288
+ * if (mixpanel.flags.areFlagsReady()) {
289
+ * if (mixpanel.flags.isEnabledSync('new-checkout', false)) {
290
+ * showNewCheckout();
291
+ * } else {
292
+ * showLegacyCheckout();
293
+ * }
294
+ * }
295
+ *
296
+ * @example
297
+ * // With explicit fallback
298
+ * const enableBetaFeatures = mixpanel.flags.isEnabledSync('beta-features', true);
299
+ *
300
+ * @see isEnabled for asynchronous access
301
+ * @see getVariantValueSync for non-boolean flag values
96
302
  */
97
303
  isEnabledSync(featureName, fallbackValue = false) {
98
304
  if (!this.areFlagsReady()) {
@@ -109,11 +315,47 @@ export class Flags {
109
315
 
110
316
  /**
111
317
  * Get a feature flag variant asynchronously.
112
- * Supports both callback and Promise patterns.
113
- * @param {string} featureName - Name of the feature flag
114
- * @param {object} fallback - Fallback variant if flag is not available
115
- * @param {function} callback - Optional callback function
116
- * @returns {Promise|void} Promise if no callback provided, void otherwise
318
+ *
319
+ * <p>Returns the complete variant object for a feature flag, including both the variant key
320
+ * and the variant value. This method works regardless of whether flags are ready, making it
321
+ * safe to use at any time.
322
+ *
323
+ * <p>Supports both Promise and callback patterns for maximum flexibility.
324
+ *
325
+ * <p>When a flag is evaluated for the first time, Mixpanel automatically tracks a
326
+ * "$experiment_started" event with relevant experiment metadata.
327
+ *
328
+ * @param {string} featureName The unique identifier for the feature flag
329
+ * @param {object} fallback The fallback variant object to return if the flag is not available.
330
+ * Must include both 'key' and 'value' properties.
331
+ * @param {function} [callback] Optional callback function that receives the variant object.
332
+ * If provided, the method returns void. If omitted, the method returns a Promise.
333
+ * @returns {Promise<object>|void} Promise that resolves to the variant object if no callback provided,
334
+ * void if callback is provided. The variant object has the following structure:
335
+ * - key: {string} The variant key (e.g., "control", "treatment")
336
+ * - value: {any} The variant value (can be any JSON-serializable type)
337
+ * - experiment_id: {string|number} (optional) The experiment ID if this is an experiment
338
+ * - is_experiment_active: {boolean} (optional) Whether the experiment is currently active
339
+ *
340
+ * @example
341
+ * // Promise pattern (recommended)
342
+ * const variant = await mixpanel.flags.getVariant('checkout-flow', {
343
+ * key: 'control',
344
+ * value: 'standard'
345
+ * });
346
+ * console.log(`Using ${variant.key}: ${variant.value}`);
347
+ *
348
+ * @example
349
+ * // Callback pattern
350
+ * mixpanel.flags.getVariant('pricing-test', {
351
+ * key: 'default',
352
+ * value: { price: 9.99 }
353
+ * }, (variant) => {
354
+ * console.log(`Price: ${variant.value.price}`);
355
+ * });
356
+ *
357
+ * @see getVariantSync for synchronous access when flags are ready
358
+ * @see getVariantValue to get only the value without the variant key
117
359
  */
118
360
  getVariant(featureName, fallback, callback) {
119
361
  // If callback provided, use callback pattern
@@ -150,11 +392,45 @@ export class Flags {
150
392
 
151
393
  /**
152
394
  * Get a feature flag variant value asynchronously.
153
- * Supports both callback and Promise patterns.
154
- * @param {string} featureName - Name of the feature flag
155
- * @param {any} fallbackValue - Fallback value if flag is not available
156
- * @param {function} callback - Optional callback function
157
- * @returns {Promise|void} Promise if no callback provided, void otherwise
395
+ *
396
+ * <p>Returns only the value portion of a feature flag variant. This method works regardless
397
+ * of whether flags are ready, making it safe to use at any time.
398
+ *
399
+ * <p>Supports both Promise and callback patterns for maximum flexibility.
400
+ *
401
+ * <p>When a flag is evaluated for the first time, Mixpanel automatically tracks a
402
+ * "$experiment_started" event with relevant experiment metadata.
403
+ *
404
+ * @param {string} featureName The unique identifier for the feature flag
405
+ * @param {any} fallbackValue The fallback value to return if the flag is not available.
406
+ * Can be any JSON-serializable type (string, number, boolean, object, array, etc.)
407
+ * @param {function} [callback] Optional callback function that receives the flag value.
408
+ * If provided, the method returns void. If omitted, the method returns a Promise.
409
+ * @returns {Promise<any>|void} Promise that resolves to the flag value if no callback provided,
410
+ * void if callback is provided. The return type matches the type of value configured in
411
+ * your Mixpanel project.
412
+ *
413
+ * @example
414
+ * // Promise pattern (recommended)
415
+ * const buttonColor = await mixpanel.flags.getVariantValue('button-color', 'blue');
416
+ * applyButtonColor(buttonColor);
417
+ *
418
+ * @example
419
+ * // Promise pattern with object value
420
+ * const pricing = await mixpanel.flags.getVariantValue('pricing-config', {
421
+ * price: 9.99,
422
+ * currency: 'USD'
423
+ * });
424
+ * displayPrice(pricing.price, pricing.currency);
425
+ *
426
+ * @example
427
+ * // Callback pattern
428
+ * mixpanel.flags.getVariantValue('theme', 'light', (theme) => {
429
+ * applyTheme(theme);
430
+ * });
431
+ *
432
+ * @see getVariantValueSync for synchronous access when flags are ready
433
+ * @see getVariant to get the full variant object including key and metadata
158
434
  */
159
435
  getVariantValue(featureName, fallbackValue, callback) {
160
436
  // If callback provided, use callback pattern
@@ -191,11 +467,47 @@ export class Flags {
191
467
 
192
468
  /**
193
469
  * Check if a feature flag is enabled asynchronously.
194
- * Supports both callback and Promise patterns.
195
- * @param {string} featureName - Name of the feature flag
196
- * @param {boolean} [fallbackValue=false] - Fallback value if flag is not available
197
- * @param {function} [callback] - Optional callback function
198
- * @returns {Promise<boolean>|void} Promise if no callback provided, void otherwise
470
+ *
471
+ * <p>This is a convenience method for boolean feature flags. It checks if a feature is enabled
472
+ * by evaluating the variant value as a boolean. This method works regardless of whether flags
473
+ * are ready, making it safe to use at any time.
474
+ *
475
+ * <p>Supports both Promise and callback patterns for maximum flexibility.
476
+ *
477
+ * <p>When a flag is evaluated for the first time, Mixpanel automatically tracks a
478
+ * "$experiment_started" event with relevant experiment metadata.
479
+ *
480
+ * @param {string} featureName The unique identifier for the feature flag
481
+ * @param {boolean} [fallbackValue=false] The fallback value to return if the flag is not available.
482
+ * Defaults to false if not provided.
483
+ * @param {function} [callback] Optional callback function that receives the boolean result.
484
+ * If provided, the method returns void. If omitted, the method returns a Promise.
485
+ * @returns {Promise<boolean>|void} Promise that resolves to true if enabled, false otherwise
486
+ * (when no callback provided). Returns void if callback is provided.
487
+ *
488
+ * @example
489
+ * // Promise pattern (recommended)
490
+ * const isEnabled = await mixpanel.flags.isEnabled('new-checkout', false);
491
+ * if (isEnabled) {
492
+ * showNewCheckout();
493
+ * } else {
494
+ * showLegacyCheckout();
495
+ * }
496
+ *
497
+ * @example
498
+ * // Callback pattern
499
+ * mixpanel.flags.isEnabled('beta-features', false, (isEnabled) => {
500
+ * if (isEnabled) {
501
+ * enableBetaFeatures();
502
+ * }
503
+ * });
504
+ *
505
+ * @example
506
+ * // Default fallback (false)
507
+ * const showPromo = await mixpanel.flags.isEnabled('show-promo');
508
+ *
509
+ * @see isEnabledSync for synchronous access when flags are ready
510
+ * @see getVariantValue for non-boolean flag values
199
511
  */
200
512
  isEnabled(featureName, fallbackValue = false, callback) {
201
513
  // If callback provided, use callback pattern
@@ -231,16 +543,50 @@ export class Flags {
231
543
  }
232
544
 
233
545
  /**
234
- * Update the context used for feature flag evaluation
235
- * Aligned with mixpanel-js API
546
+ * Update the context used for feature flag evaluation.
547
+ *
548
+ * <p>Context properties are used to determine which feature flag variants a user should receive
549
+ * based on targeting rules configured in your Mixpanel project. This allows for personalized
550
+ * feature experiences based on user attributes, device properties, or custom criteria.
551
+ *
552
+ * <p><b>IMPORTANT LIMITATION:</b> This method is <b>only available in JavaScript mode</b>
553
+ * (Expo/React Native Web). In native mode (iOS/Android), context must be set during initialization
554
+ * via {@link Mixpanel#init} and cannot be updated at runtime.
555
+ *
556
+ * <p>By default, the new context properties are merged with existing context. Set
557
+ * <code>options.replace = true</code> to completely replace the context instead.
236
558
  *
237
- * NOTE: This method is only available in JavaScript mode (Expo/React Native Web).
238
- * In native mode, context must be set during initialization via FeatureFlagsOptions.
559
+ * @param {object} newContext New context properties to add or update. Can include any
560
+ * JSON-serializable properties that are used in your feature flag targeting rules.
561
+ * Common examples include user tier, region, platform version, etc.
562
+ * @param {object} [options={replace: false}] Configuration options for the update
563
+ * @param {boolean} [options.replace=false] If true, replaces the entire context instead of merging.
564
+ * If false (default), merges new properties with existing context.
565
+ * @returns {Promise<void>} A promise that resolves when the context has been updated and
566
+ * flags have been re-evaluated with the new context
567
+ * @throws {Error} if called in native mode (iOS/Android)
239
568
  *
240
- * @param {object} newContext - New context properties to add/update
241
- * @param {object} options - Options object
242
- * @param {boolean} options.replace - If true, replace entire context instead of merging
243
- * @returns {Promise<void>}
569
+ * @example
570
+ * // Merge new properties into existing context (JavaScript mode only)
571
+ * await mixpanel.flags.updateContext({
572
+ * user_tier: 'premium',
573
+ * region: 'us-west'
574
+ * });
575
+ *
576
+ * @example
577
+ * // Replace entire context (JavaScript mode only)
578
+ * await mixpanel.flags.updateContext({
579
+ * device_type: 'tablet',
580
+ * os_version: '14.0'
581
+ * }, { replace: true });
582
+ *
583
+ * @example
584
+ * // This will throw an error in native mode
585
+ * try {
586
+ * await mixpanel.flags.updateContext({ tier: 'premium' });
587
+ * } catch (error) {
588
+ * console.error('Context updates not supported in native mode');
589
+ * }
244
590
  */
245
591
  async updateContext(newContext, options = { replace: false }) {
246
592
  if (this.isNativeMode) {
@@ -256,34 +602,68 @@ export class Flags {
256
602
  }
257
603
 
258
604
  // snake_case aliases for API consistency with mixpanel-js
605
+
606
+ /**
607
+ * Alias for {@link areFlagsReady}. Provided for API consistency with mixpanel-js.
608
+ * @see areFlagsReady
609
+ */
259
610
  are_flags_ready() {
260
611
  return this.areFlagsReady();
261
612
  }
262
613
 
614
+ /**
615
+ * Alias for {@link getVariant}. Provided for API consistency with mixpanel-js.
616
+ * @see getVariant
617
+ */
263
618
  get_variant(featureName, fallback, callback) {
264
619
  return this.getVariant(featureName, fallback, callback);
265
620
  }
266
621
 
622
+ /**
623
+ * Alias for {@link getVariantSync}. Provided for API consistency with mixpanel-js.
624
+ * @see getVariantSync
625
+ */
267
626
  get_variant_sync(featureName, fallback) {
268
627
  return this.getVariantSync(featureName, fallback);
269
628
  }
270
629
 
630
+ /**
631
+ * Alias for {@link getVariantValue}. Provided for API consistency with mixpanel-js.
632
+ * @see getVariantValue
633
+ */
271
634
  get_variant_value(featureName, fallbackValue, callback) {
272
635
  return this.getVariantValue(featureName, fallbackValue, callback);
273
636
  }
274
637
 
638
+ /**
639
+ * Alias for {@link getVariantValueSync}. Provided for API consistency with mixpanel-js.
640
+ * @see getVariantValueSync
641
+ */
275
642
  get_variant_value_sync(featureName, fallbackValue) {
276
643
  return this.getVariantValueSync(featureName, fallbackValue);
277
644
  }
278
645
 
646
+ /**
647
+ * Alias for {@link isEnabled}. Provided for API consistency with mixpanel-js.
648
+ * @see isEnabled
649
+ */
279
650
  is_enabled(featureName, fallbackValue, callback) {
280
651
  return this.isEnabled(featureName, fallbackValue, callback);
281
652
  }
282
653
 
654
+ /**
655
+ * Alias for {@link isEnabledSync}. Provided for API consistency with mixpanel-js.
656
+ * @see isEnabledSync
657
+ */
283
658
  is_enabled_sync(featureName, fallbackValue) {
284
659
  return this.isEnabledSync(featureName, fallbackValue);
285
660
  }
286
661
 
662
+ /**
663
+ * Alias for {@link updateContext}. Provided for API consistency with mixpanel-js.
664
+ * JavaScript mode only.
665
+ * @see updateContext
666
+ */
287
667
  update_context(newContext, options) {
288
668
  return this.updateContext(newContext, options);
289
669
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-react-native",
3
- "version": "3.2.0-beta.1",
3
+ "version": "3.2.0-beta.2",
4
4
  "description": "Official React Native Tracking Library for Mixpanel Analytics",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -38,6 +38,7 @@
38
38
  "eslint": "^7.11.0",
39
39
  "jest": "^26.6.3",
40
40
  "jest-fetch-mock": "^3.0.3",
41
+ "jsdoc": "^4.0.5",
41
42
  "metro-react-native-babel-preset": "^0.63.0",
42
43
  "react-native": "^0.63.3",
43
44
  "react-test-renderer": "16.13.1"