npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@upfluxhq/react-native

v1.2.0

Published

React Native SDK for Upflux OTA updates

Readme

@upfluxhq/react-native

React Native SDK for Upflux OTA updates.

Installation

npm install @upfluxhq/react-native
# or
yarn add @upfluxhq/react-native
# or
pnpm add @upfluxhq/react-native

iOS Setup

cd ios && pod install

Native Module Setup (Required for OTA to work)

The native module is autolinked automatically when you install the package. However, you need to configure your app to load the OTA bundle.

Android Setup

The UpfluxPackage is autolinked automatically. You only need to modify MainApplication.kt to load the custom bundle path.

MainApplication.kt (React Native 0.73+):

import com.upflux.UpfluxModule

class MainApplication : Application(), ReactApplication {

  override val reactHost: ReactHost by lazy {
    // Get custom bundle path from Upflux (returns null if no OTA update)
    val customBundlePath = UpfluxModule.getJSBundleFile(this)
    
    getDefaultReactHost(
      context = applicationContext,
      packageList = PackageList(this).packages,
      jsBundleFilePath = customBundlePath
    )
  }

  override fun onCreate() {
    super.onCreate()
    loadReactNative(this)
  }
}

MainApplication.kt (React Native < 0.73 Legacy):

import com.upflux.UpfluxModule

// Add this method to your MainApplication class:
override fun getJSBundleFile(): String? {
    return UpfluxModule.getJSBundleFile(this) ?: super.getJSBundleFile()
}

MainApplication.java (for Java-based projects):

import com.upflux.UpfluxModule;

// Add this method to your MainApplication class:
@Override
protected String getJSBundleFile() {
    String customBundle = UpfluxModule.getJSBundleFile(this);
    if (customBundle != null) {
        return customBundle;
    }
    return super.getJSBundleFile();
}

iOS Setup

The native module is autolinked via CocoaPods. Run pod install after installing the package:

cd ios && pod install

Then modify AppDelegate.m (or AppDelegate.mm) to load the OTA bundle:

#import <upflux_react_native/UpfluxModule.h>

// Modify sourceURLForBridge:
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
    // Check for OTA bundle first
    NSURL *customBundle = [UpfluxModule bundleURL];
    if (customBundle) {
        return customBundle;
    }
    
    // Fall back to default bundle
    return [self bundleURL];
}

AppDelegate.swift (for Swift-based projects):

First, ensure your bridging header includes the Upflux module:

// YourApp-Bridging-Header.h
#import <upflux_react_native/UpfluxModule.h>

Then modify your AppDelegate.swift:

override func sourceURL(for bridge: RCTBridge) -> URL? {
    if let customBundle = UpfluxModule.bundleURL() {
        return customBundle
    }
    return bundleURL()
}

Usage

Basic Setup

import { Upflux } from '@upfluxhq/react-native';

// Configure the SDK
const upflux = new Upflux({
    appId: 'your-app-id',
    deployment: 'production',
    clientKey: 'your-client-key',
    serverUrl: 'https://your-api-server.com'
});

// Initialize on app startup
await upflux.init();

Note: The binaryVersion is automatically detected from the native platform (versionName on Android, CFBundleShortVersionString on iOS). You do not need to pass it manually.

Check for Updates

const updateInfo = await upflux.checkForUpdates();

if (updateInfo.updateAvailable) {
    console.log('Update available:', updateInfo.releaseLabel);
}

Download and Apply Update

if (updateInfo.updateAvailable) {
    // Download the update
    const downloadResult = await upflux.downloadUpdate(updateInfo, {
        onProgress: (progress) => {
            console.log(`Download progress: ${progress.percentage}%`);
        }
    });

    if (downloadResult.success) {
        // Apply and restart
        await upflux.applyUpdate(downloadResult);
        // App will restart here
    }
}

Complete Example

import React, { useEffect, useState } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import { Upflux } from '@upfluxhq/react-native';

const upflux = new Upflux({
    appId: 'my-app',
    deployment: 'production',
    clientKey: 'your-client-key',
    serverUrl: 'http://10.0.2.2:4000' // For Android emulator
});

export default function App() {
    const [checking, setChecking] = useState(false);
    const [updating, setUpdating] = useState(false);

    useEffect(() => {
        upflux.init();
    }, []);

    const checkForUpdates = async () => {
        setChecking(true);
        try {
            const updateInfo = await upflux.checkForUpdates();

            if (updateInfo.updateAvailable) {
                Alert.alert(
                    'Update Available',
                    `Version ${updateInfo.releaseLabel} is available`,
                    [
                        { text: 'Later', style: 'cancel' },
                        { text: 'Update Now', onPress: () => installUpdate(updateInfo) }
                    ]
                );
            } else {
                Alert.alert('Up to Date', 'You have the latest version');
            }
        } catch (error) {
            console.error('Error checking for updates:', error);
        } finally {
            setChecking(false);
        }
    };

    const installUpdate = async (updateInfo) => {
        setUpdating(true);
        try {
            const downloadResult = await upflux.downloadUpdate(updateInfo);
            
            if (downloadResult.success) {
                await upflux.applyUpdate(downloadResult);
                // App will restart
            }
        } catch (error) {
            console.error('Error installing update:', error);
            Alert.alert('Error', 'Failed to install update');
        } finally {
            setUpdating(false);
        }
    };

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <Text>My App</Text>
            <Button 
                title={checking ? 'Checking...' : 'Check for Updates'}
                onPress={checkForUpdates}
                disabled={checking || updating}
            />
        </View>
    );
}

Binary Upgrade Detection

The SDK automatically detects when a user upgrades or downgrades the app via the Play Store or App Store, and clears any stale OTA bundles so the new native binary's default bundle is used.

How It Works

The SDK uses a two-layer defense to detect binary version changes:

  1. Native Layer (before JS loads): getJSBundleFile() (Android) and bundleURL (iOS) compare the native version stored when the OTA was applied against the current native version. If they differ, the stale OTA path is cleared and the default bundle loads. This ensures the correct bundle is used on the very first launch after an upgrade.

  2. JS Layer (after JS loads): resolveInitialBundle() performs the same check and additionally cleans up stale metadata (activeBundle.json) and deletes old bundle files from disk to free storage.

Version Auto-Detection

The binaryVersion sent to the server during update checks is automatically read from the native platform:

  • Android: PackageInfo.versionName (from build.gradle)
  • iOS: CFBundleShortVersionString (from Info.plist)

This means the SDK always reports the correct binary version, even when running from an OTA bundle that was built for a previous version. You never need to manually set this value.

API Reference

Upflux Class

constructor(config: UpfluxConfig)

Create a new Upflux instance.

interface UpfluxConfig {
    appId: string;        // Your app identifier
    deployment: string;   // Deployment name (e.g., 'production', 'staging')
    clientKey: string;    // Client key from dashboard
    serverUrl: string;    // Upflux API server URL
    collectDeviceInfo?: boolean; // Whether to collect device info (default: true)
}

init(): Promise<StartupBundle>

Initialize the SDK. Call this early in app startup. Resolves which bundle to load and detects binary upgrades.

checkForUpdates(deviceInfo?: Partial<DeviceInfo>): Promise<UpdateInfo>

Check if an update is available. Binary version is auto-detected from the native platform.

downloadUpdate(info: UpdateInfo, options?: DownloadOptions): Promise<DownloadResult>

Download an available update.

applyUpdate(result: DownloadResult): Promise<ApplyResult>

Apply a downloaded update and restart the app.

clearUpdate(): Promise<void>

Clear the current update and revert to default bundle on next restart.

isInitialized(): boolean

Check if the SDK has been initialized.

getStartupBundle(): StartupBundle | null

Get the startup bundle info from init().

getConfig(): UpfluxConfig

Get the current SDK configuration.

destroy(): void

Destroy the SDK instance and clean up resources.

Store Update Required

When a release is published with --min-binary-version, the server returns storeUpdateRequired: true for devices whose native binary is below that version. Check it directly on the checkForUpdates() return value:

const info = await upflux.checkForUpdates();

if (info.storeUpdateRequired) {
    // Device binary is too old — direct user to the store
    Alert.alert(
        'App Update Required',
        `Please update the app to v${info.requiredBinaryVersion} from the store first.`,
        [{ text: 'Update Now', onPress: () => Linking.openURL('https://play.google.com/...') }]
    );
} else if (info.updateAvailable) {
    // Normal OTA flow
    const result = await upflux.downloadUpdate(info);
    await upflux.applyUpdate(result);
}

Storage Location

Updates are stored in the app's document directory:

<DocumentDirectory>/upflux/
├── activeBundle.json          # Currently active release info
├── deviceId.txt               # Persistent device identifier
├── v1.0.0/                    # Release directory
│   ├── bundle.js             # JS bundle
│   └── assets/               # Assets
└── v1.0.1/                    # Another release

When a binary upgrade is detected, all old bundle directories are automatically cleaned up to free storage.

Troubleshooting

Network Error on Android Emulator

Use 10.0.2.2 instead of localhost:

serverUrl: 'http://10.0.2.2:4000'

Bundle Not Loading After Restart

Make sure you've completed the native module setup (see above).

Update Downloads But Doesn't Apply

Check that:

  1. Native module is properly registered
  2. Bundle path override is implemented in AppDelegate/MainApplication
  3. File permissions allow reading from document directory

Stale OTA After Play Store Update

This is automatically handled in v1.1.0+. The SDK detects binary version changes at the native level and clears stale OTA bundles before any JS code runs. If you're upgrading from an earlier version, simply update the SDK — no code changes needed (except removing binaryVersion from your config if you were passing it).

Migration from v1.0.x

Breaking Changes in v1.1.0

binaryVersion removed from UpfluxConfig:

The binaryVersion field has been removed from the configuration. It is now automatically detected from the native platform. Remove it from your config:

 const upflux = new Upflux({
     appId: 'my-app',
     deployment: 'production',
     clientKey: 'your-client-key',
     serverUrl: 'https://api.example.com',
-    binaryVersion: '1.0.0',  // Remove this line
 });

checkForUpdates() no longer needs binaryVersion:

- const updateInfo = await upflux.checkForUpdates({
-     deviceId: 'device-123',
-     binaryVersion: '1.0.0',
- });
+ const updateInfo = await upflux.checkForUpdates();

License

MIT