diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index f8944fa5..e546f2c3 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -27,7 +27,7 @@ jobs: - run: npm test test_android: - runs-on: ubuntu-latest + runs-on: macos-15-intel strategy: matrix: node-version: [18.x] @@ -50,16 +50,10 @@ jobs: - name: Test Integration - Install dependencies working-directory: ./Samples/SimpleMixpanel run: yarn install - - name: Enable KVM - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - name: Setup Android uses: reactivecircus/android-emulator-runner@v2.32.0 with: api-level: 34 - target: google_apis profile: Nexus 5X arch: x86_64 working-directory: ./Samples/SimpleMixpanel/android @@ -68,7 +62,6 @@ jobs: uses: reactivecircus/android-emulator-runner@v2.32.0 with: api-level: 34 - target: google_apis profile: Nexus 5X arch: x86_64 working-directory: ./Samples/SimpleMixpanel @@ -95,6 +88,29 @@ jobs: - name: Setup iOS working-directory: ./Samples/SimpleMixpanel/ios run: pod install --repo-update + - name: List available simulators + run: xcrun simctl list devices available + - name: Boot iOS Simulator + run: | + # Get list of available iPhone simulators + # Note: jq is pre-installed on macOS GitHub Actions runners + SIMULATOR_ID=$(xcrun simctl list devices available -j | jq -r '.devices | to_entries[] | .value[] | select(.name | contains("iPhone")) | .udid' | head -n 1) + if [ -z "$SIMULATOR_ID" ]; then + echo "Error: No iPhone simulator found" + exit 1 + fi + echo "Found simulator: $SIMULATOR_ID" + # Check if simulator is already booted + DEVICE_LIST=$(xcrun simctl list devices) + if echo "$DEVICE_LIST" | grep -q "$SIMULATOR_ID.*Booted"; then + echo "Simulator already booted" + else + echo "Booting simulator..." + xcrun simctl boot "$SIMULATOR_ID" + fi + # Verify simulator is booted + echo "Booted simulators:" + xcrun simctl list devices | grep Booted - name: Test iOS working-directory: ./Samples/SimpleMixpanel run: react-native run-ios diff --git a/.npmignore b/.npmignore index 211eb7f9..a3233268 100644 --- a/.npmignore +++ b/.npmignore @@ -45,3 +45,49 @@ buck-out/ # DemoApp /MixpanelDemo + +# Sample Apps +Samples/ + +# Tests +__tests__/ +__mocks__/ +*.test.js +jest.config.js + +# Documentation (generated) +docs/ +generate_docs.sh + +# Build artifacts +*.log +*.tgz + +# AI Assistant Files +.claude/ +claude/ +.cursor/ +.github/copilot-* +.github/instructions/ +.github/prompts/ +.github/workflows/ +CLAUDE.md + +# IDE +.vscode/ +.idea/ + +# Git +.git/ +.gitignore +.gitattributes + +# Python scripts +*.py + +# Misc +.editorconfig +.prettierrc* +.eslintrc* +.babelrc* +.flowconfig diff --git a/FEATURE_FLAGS_QUICKSTART.md b/FEATURE_FLAGS_QUICKSTART.md new file mode 100644 index 00000000..ce251951 --- /dev/null +++ b/FEATURE_FLAGS_QUICKSTART.md @@ -0,0 +1,348 @@ +# Feature Flags Quick Start Guide (Beta) + +> **Beta Version:** `3.2.0-beta.2` +> **Native Mode Only:** This beta release supports iOS and Android native implementations. JavaScript mode (Expo/React Native Web) support coming in future release. + +## Installation + +Install the beta version: + +```bash +npm install mixpanel-react-native@beta +``` + +For iOS, update native dependencies: + +```bash +cd ios && pod install +``` + +## Basic Setup + +### 1. Initialize with Feature Flags Enabled + +```javascript +import { Mixpanel } from 'mixpanel-react-native'; + +const mixpanel = new Mixpanel('YOUR_TOKEN'); + +// Enable Feature Flags during initialization +await mixpanel.init( + false, // optOutTrackingDefault + {}, // superProperties + 'https://api.mixpanel.com', // serverURL + true, // useGzipCompression + { + enabled: true, // Enable Feature Flags + context: { // Optional: Add targeting context + platform: 'mobile', + app_version: '2.1.0' + } + } +); +``` + +### 2. Check Flag Availability + +Before accessing flags, verify they're loaded: + +```javascript +if (mixpanel.flags.areFlagsReady()) { + // Flags are ready to use + console.log('Feature flags loaded!'); +} +``` + +## Using Feature Flags + +### Synchronous API (Recommended for UI) + +Use sync methods when flags are ready (e.g., in render methods): + +```javascript +// Check if feature is enabled +const showNewUI = mixpanel.flags.isEnabledSync('new-checkout-flow', false); + +// Get variant value directly +const buttonColor = mixpanel.flags.getVariantValueSync('button-color', 'blue'); + +// Get full variant object with metadata +const variant = mixpanel.flags.getVariantSync('pricing-tier', { + key: 'control', + value: 'standard' +}); + +console.log(`Variant: ${variant.key}, Value: ${variant.value}`); +if (variant.experiment_id) { + console.log(`Part of experiment: ${variant.experiment_id}`); +} +``` + +### Asynchronous API (Promise Pattern) + +Use async methods for event handlers or initialization: + +```javascript +// Promise pattern +const variant = await mixpanel.flags.getVariant('checkout-flow', { + key: 'control', + value: 'standard' +}); + +const enabled = await mixpanel.flags.isEnabled('dark-mode', false); + +const colorValue = await mixpanel.flags.getVariantValue('theme-color', '#0000FF'); +``` + +### Asynchronous API (Callback Pattern) + +Alternative callback style for compatibility: + +```javascript +// Callback pattern +mixpanel.flags.getVariant('feature-name', { key: 'control', value: 'off' }, (variant) => { + console.log(`Feature variant: ${variant.key}`); +}); + +mixpanel.flags.isEnabled('new-feature', false, (isEnabled) => { + if (isEnabled) { + // Show new feature + } +}); +``` + +## Real-World Examples + +### Example 1: Feature Toggle + +```javascript +const NewCheckoutButton = () => { + const [showNewCheckout, setShowNewCheckout] = useState(false); + + useEffect(() => { + // Load flags on mount + if (mixpanel.flags.areFlagsReady()) { + const enabled = mixpanel.flags.isEnabledSync('new-checkout', false); + setShowNewCheckout(enabled); + } + }, []); + + return showNewCheckout ? : ; +}; +``` + +### Example 2: A/B Test with Variants + +```javascript +const ProductCard = ({ product }) => { + // Get button color variant (A/B test) + const buttonColor = mixpanel.flags.areFlagsReady() + ? mixpanel.flags.getVariantValueSync('button-color', 'blue') + : 'blue'; + + // Get pricing display variant + const pricingVariant = mixpanel.flags.areFlagsReady() + ? mixpanel.flags.getVariantSync('pricing-display', { + key: 'control', + value: 'standard' + }) + : { key: 'control', value: 'standard' }; + + return ( + + {product.name} + {pricingVariant.value === 'bold' ? ( + ${product.price} + ) : ( + ${product.price} + )} +