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

@poilabs/analysis-sdk-plugin

v1.5.2

Published

Official Expo Config Plugin for integrating the Poilabs Analysis SDK

Downloads

274

Readme

@poilabs/analysis-sdk-plugin

Official Expo Config Plugin for integrating the Poilabs Analysis SDK into Expo (prebuild) projects.

🚀 Automatically links native dependencies and modifies required iOS/Android files.


✨ What this plugin does

When used with expo prebuild, this plugin:

  • Android Permissions: Adds all required permissions to AndroidManifest.xml
    • Including REQUEST_IGNORE_BATTERY_OPTIMIZATIONS for doze mode prevention
    • Adds android:foregroundServiceType="location" to the FOREGROUND_SERVICE permission
  • Android Native Integration: Automatically configures native modules
    • Copies and registers PoilabsPackage in MainApplication.kt
    • Adds Poilabs SDK dependency to android/app/build.gradle
    • Configures JitPack repository in android/build.gradle
  • iOS Integration:
    • Adds pod 'PoilabsAnalysis' to the iOS Podfile
    • Adds Info.plist keys for Location and Bluetooth usage
  • Foreground Service Setup: Automatically configures foreground service intent for doze mode prevention

📦 Installation

Install the plugin to your Expo project:

npm install @poilabs/analysis-sdk-plugin
# or
yarn add @poilabs/analysis-sdk-plugin

Also install the required dependencies:

npx expo install expo-location expo-device

⚙️ Configuration

Add the plugin to your app.json or app.config.js:

{
  "expo": {
    "plugins": [
      [
        "@poilabs/analysis-sdk-plugin",
        {
          "jitpackToken": "YOUR_JITPACK_TOKEN" // Get this from Poilabs
        }
      ]
    ]
  }
}

Then run the prebuild command:

npx expo prebuild

Android Setup (Automatic! 🎉)

The plugin now automatically handles Android setup:

Auto-registers PoilabsPackage in MainApplication.kt
Auto-adds REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission
Auto-configures foreground service intent

Simply run:

npx expo prebuild
npx expo run:android

That's it! No manual code changes needed.

⚠️ Android local.properties Warning

  • You should create local.properties to android root
  • and you should add this => sdk.dir=/Users/USERNAME/Library/Android/sdk

iOS Setup

For iOS, you need to ensure the plugin files are properly included in your Xcode project:

  1. Open your Xcode project
  2. In Xcode, verify that the PoilabsModule files are added to your project
  3. Check that the files appear in the "Build Phases > Compile Sources" section
  4. Find + button and click. Then you should "add other".
  5. If files are missing, you may need to manually add them from the iOS//PoilabsModule directory:
    • PoilabsAnalysisModule.h
    • PoilabsAnalysisModule.m

Pods Setup

  • cd ios
  • pod deintegrate
  • pod install --repo-update

⚠️ iOS ARM64 Warning

Note: When developing for iOS, there's an important consideration regarding ARM64 architecture:

  • If you're integrating into an existing project (which already has ARM64 support), you shouldn't encounter any issues.
  • However, if you're creating a project from scratch, you need to remove the ARM64 reference from the Build Settings in Xcode. Otherwise, you might face compilation errors.

This setting is particularly important when developing on M series (Apple Silicon) Mac computers.

Then build and run your iOS project:

npx expo run:ios

🚀 Usage

After the prebuild process, you can use the SDK in your application:

import { Image } from "expo-image";
import { useEffect, useState } from "react";
import { StyleSheet, TouchableOpacity } from "react-native";

import { HelloWave } from "@/components/HelloWave";
import ParallaxScrollView from "@/components/ParallaxScrollView";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import {
  startPoilabsAnalysis,
  stopPoilabsAnalysis,
} from "@poilabs/analysis-sdk-plugin";

export default function HomeScreen() {
  const [sdkStatus, setSdkStatus] = useState("Initializing...");

  useEffect(() => {
    const initAnalysis = async () => {
      try {
        // Start Poilabs SDK
        const success = await startPoilabsAnalysis({
          applicationId: "YOUR_APPLICATION_ID", // Get from Poilabs
          applicationSecret: "YOUR_APPLICATION_SECRET", // Get from Poilabs
          uniqueId: "USER_UNIQUE_ID", // A unique identifier for the user
        });

        setSdkStatus(success ? "Running ✅" : "Failed to start ❌");
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        setSdkStatus("Error: " + errorMessage);
      }
    };

    initAnalysis();
  }, []);

  const handleStopSDK = () => {
    try {
      const result = stopPoilabsAnalysis();
      setSdkStatus(result ? "Stopped ⛔" : "Failed to stop ❓");
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      setSdkStatus("Stop Error: " + errorMessage);
    }
  };

  return (
    <ParallaxScrollView
      headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
      headerImage={
        <Image
          source={require("@/assets/images/partial-react-logo.png")}
          style={styles.reactLogo}
        />
      }
    >
      <ThemedView style={styles.titleContainer}>
        <ThemedText type="title">Welcome!</ThemedText>
        <HelloWave />
      </ThemedView>

      {/* SDK Status Indicator */}
      <ThemedView style={styles.sdkStatusContainer}>
        <ThemedText type="subtitle">Poilabs SDK: {sdkStatus}</ThemedText>

        {/* Stop SDK Button */}
        <TouchableOpacity style={styles.stopButton} onPress={handleStopSDK}>
          <ThemedText style={styles.buttonText}>STOP SDK</ThemedText>
        </TouchableOpacity>
      </ThemedView>
    </ParallaxScrollView>
  );
}

const styles = StyleSheet.create({
  titleContainer: {
    flexDirection: "row",
    alignItems: "center",
    gap: 8,
  },
  reactLogo: {
    height: 178,
    width: 290,
    bottom: 0,
    left: 0,
    position: "absolute",
  },
  sdkStatusContainer: {
    marginVertical: 16,
    padding: 10,
    borderRadius: 8,
    backgroundColor: "#f5f5f5",
    alignItems: "center",
  },
  stopButton: {
    marginTop: 10,
    backgroundColor: "#FF3B30",
    paddingVertical: 8,
    paddingHorizontal: 16,
    borderRadius: 6,
  },
  buttonText: {
    color: "white",
    fontWeight: "bold",
  },
});

🔋 Doze Mode Prevention (Android)

The plugin automatically configures the foreground service intent when you start the SDK. This enables doze mode prevention right out of the box!

For apps that need continuous beacon scanning, you can optionally customize the foreground service notification:

import { configureAnalysisSDK } from "@poilabs/analysis-sdk-plugin";

// Optional: Customize foreground service notification
await configureAnalysisSDK({
  enabled: true,
  enableForegroundService: true,
  serviceNotificationTitle: 'Poilabs SDK Active',
  notificationChannelName: 'Poilabs SDK Service',
  notificationChannelDescription: 'Keeps Poilabs SDK running during doze mode'
});

What happens automatically:

  • ✅ Foreground service intent is auto-configured on SDK startup
  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission is auto-added
  • ✅ Android's doze mode won't stop beacon scanning
  • ✅ Continuous analytics even when device is idle

Why this is important:

  • Android's doze mode stops background processes including beacon scanning
  • The SDK has built-in foreground service support to prevent this
  • Essential for production apps that need reliable beacon detection

📡 iOS Monitoring Events (New in v1.5.0)

Monitor real-time beacon detection, server responses, and SDK status updates on iOS. These features allow you to track SDK activity and handle events in your React Native application.

Basic Monitoring Setup

import {
  startPoilabsAnalysis,
  addMonitoringListener,
  removeAllListeners
} from '@poilabs/analysis-sdk-plugin';
import { useEffect, useState } from 'react';

function BeaconMonitoring() {
  const [events, setEvents] = useState([]);

  useEffect(() => {
    // Initialize SDK
    const initSDK = async () => {
      await startPoilabsAnalysis({
        applicationId: 'YOUR_APP_ID',
        applicationSecret: 'YOUR_SECRET',
        uniqueId: 'user-123'
      });
    };

    // Setup event listeners
    const unsubscribeBeacon = addMonitoringListener('onBeaconDetected', (event) => {
      console.log('Beacon detected:', event);
      setEvents(prev => [...prev, { type: 'beacon', data: event }]);
    });

    const unsubscribeResponse = addMonitoringListener('onBeaconResponse', (event) => {
      console.log('Server response:', event);
      setEvents(prev => [...prev, { type: 'response', data: event }]);
    });

    const unsubscribeError = addMonitoringListener('onError', (event) => {
      console.error('SDK error:', event);
      setEvents(prev => [...prev, { type: 'error', data: event }]);
    });

    const unsubscribeStatus = addMonitoringListener('onStatusUpdate', (event) => {
      console.log('Status update:', event);
      setEvents(prev => [...prev, { type: 'status', data: event }]);
    });

    initSDK();

    // Cleanup
    return () => {
      unsubscribeBeacon();
      unsubscribeResponse();
      unsubscribeError();
      unsubscribeStatus();
      // Or use: removeAllListeners();
    };
  }, []);

  return (
    <View>
      <Text>Monitoring Events ({events.length})</Text>
      {events.map((event, index) => (
        <Text key={index}>{event.type}: {JSON.stringify(event.data)}</Text>
      ))}
    </View>
  );
}

Event Types

onBeaconDetected

Emitted when a beacon is detected by the SDK.

Event Data:

{
  beaconId?: string;
  rssi?: number;
  timestamp: number;
  uuid?: string;
  major?: number;
  minor?: number;
  source: string; // 'delegate_method' | 'notification' | 'test_method'
  [key: string]: any;
}

onBeaconResponse

Emitted when the SDK receives a response from the server about beacon monitoring.

Event Data:

{
  success: boolean;
  data?: any;
  error?: string;
  timestamp: number;
  source: string;
  [key: string]: any;
}

onError

Emitted when the SDK encounters an error.

Event Data:

{
  code: number;
  message: string;
  domain: string;
  timestamp: number;
  source: string;
}

onStatusUpdate

Emitted when the SDK status changes.

Event Data:

{
  status: 'started' | 'stopped' | 'scanning' | 'idle' | 'error' | 'unknown';
  message?: string;
  timestamp: number;
  source: string;
}

Monitoring Methods

addMonitoringListener(eventType, callback)

Adds an event listener for monitoring events.

Parameters:

  • eventType (string): One of 'onBeaconDetected', 'onBeaconResponse', 'onError', 'onStatusUpdate'
  • callback (function): Function to call when event is emitted

Returns:

  • Function: Unsubscribe function to remove the listener

Example:

const unsubscribe = addMonitoringListener('onBeaconDetected', (event) => {
  console.log('Beacon:', event.beaconId, 'RSSI:', event.rssi);
});

// Later, to remove the listener:
unsubscribe();

removeAllMonitoringListeners(eventType?)

Removes all listeners for a specific event type, or all listeners if no type is specified.

Parameters:

  • eventType (string, optional): Event type to remove listeners for

Example:

// Remove all listeners for beacon detection
removeAllMonitoringListeners('onBeaconDetected');

// Remove all listeners for all event types
removeAllMonitoringListeners();

removeAllListeners()

Removes all monitoring event listeners.

Example:

removeAllListeners();

Testing Event System

For debugging purposes, you can test if the event system is working correctly:

import { testEventEmission } from '@poilabs/analysis-sdk-plugin';

// Test event emission (iOS only)
const testResult = await testEventEmission();
if (testResult) {
  console.log('✅ Event system working - test events should be received');
} else {
  console.log('❌ Event system not working - check listeners are setup');
}

Important Notes

  • iOS Only: Monitoring events are currently only available on iOS. Android returns false with warnings.
  • Physical Device: Beacon monitoring requires a physical iOS device - it won't work in the simulator.
  • Permissions: Ensure Location and Bluetooth permissions are granted before starting monitoring.
  • Background Mode: For background monitoring, ensure proper iOS background modes are configured in your app.
  • Setup Listeners First: Always setup event listeners before calling startPoilabsAnalysis() to ensure you don't miss any events.
  • Memory Management: Always cleanup listeners when component unmounts to prevent memory leaks.

Complete Example

import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import {
  startPoilabsAnalysis,
  stopPoilabsAnalysis,
  addMonitoringListener,
  removeAllListeners
} from '@poilabs/analysis-sdk-plugin';

export default function MonitoringScreen() {
  const [events, setEvents] = useState([]);
  const [sdkStatus, setSdkStatus] = useState('initializing');

  useEffect(() => {
    let isActive = true;

    const initializeMonitoring = async () => {
      try {
        // Setup all event listeners FIRST
        addMonitoringListener('onBeaconDetected', (event) => {
          if (!isActive) return;
          console.log('🔵 Beacon:', event);
          setEvents(prev => [{ id: Date.now(), type: 'Beacon', data: event }, ...prev.slice(0, 49)]);
        });

        addMonitoringListener('onBeaconResponse', (event) => {
          if (!isActive) return;
          console.log('🌐 Response:', event);
          setEvents(prev => [{ id: Date.now(), type: 'Response', data: event }, ...prev.slice(0, 49)]);
        });

        addMonitoringListener('onError', (event) => {
          if (!isActive) return;
          console.error('❌ Error:', event);
          setEvents(prev => [{ id: Date.now(), type: 'Error', data: event }, ...prev.slice(0, 49)]);
        });

        addMonitoringListener('onStatusUpdate', (event) => {
          if (!isActive) return;
          console.log('📊 Status:', event);
          setSdkStatus(event.status);
        });

        // Then initialize SDK
        const result = await startPoilabsAnalysis({
          applicationId: 'YOUR_APP_ID',
          applicationSecret: 'YOUR_SECRET',
          uniqueId: 'user-' + Date.now()
        });

        if (result) {
          console.log('✅ SDK initialized and monitoring active');
          setSdkStatus('running');
        } else {
          console.error('❌ SDK initialization failed');
          setSdkStatus('failed');
        }
      } catch (error) {
        console.error('Initialization error:', error);
        setSdkStatus('error');
      }
    };

    initializeMonitoring();

    // Cleanup
    return () => {
      isActive = false;
      removeAllListeners();
      stopPoilabsAnalysis();
    };
  }, []);

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>Poilabs Monitoring</Text>
        <Text style={styles.status}>Status: {sdkStatus}</Text>
        <Text style={styles.count}>Events: {events.length}</Text>
      </View>

      <FlatList
        data={events}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <View style={styles.eventItem}>
            <Text style={styles.eventType}>{item.type}</Text>
            <Text style={styles.eventTime}>
              {new Date(item.data.timestamp).toLocaleTimeString()}
            </Text>
            <Text style={styles.eventData} numberOfLines={3}>
              {JSON.stringify(item.data, null, 2)}
            </Text>
          </View>
        )}
        ListEmptyComponent={
          <Text style={styles.emptyText}>
            No events yet. Get near beacons to see activity.
          </Text>
        }
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' },
  header: { padding: 20, backgroundColor: '#f5f5f5', borderBottomWidth: 1, borderBottomColor: '#ddd' },
  title: { fontSize: 24, fontWeight: 'bold', marginBottom: 5 },
  status: { fontSize: 14, color: '#666', marginBottom: 3 },
  count: { fontSize: 14, color: '#666' },
  eventItem: { padding: 15, borderBottomWidth: 1, borderBottomColor: '#eee' },
  eventType: { fontSize: 16, fontWeight: 'bold', color: '#007AFF', marginBottom: 5 },
  eventTime: { fontSize: 12, color: '#999', marginBottom: 5 },
  eventData: { fontSize: 11, fontFamily: 'monospace', color: '#333' },
  emptyText: { padding: 50, textAlign: 'center', color: '#999', fontSize: 14 }
});

📝 API Reference

startPoilabsAnalysis(config)

Starts the Poilabs Analysis SDK with the given configuration.

Parameters

  • config (Object):
    • applicationId (String): The application ID provided by Poilabs
    • applicationSecret (String): The application secret provided by Poilabs
    • uniqueId (String): A unique identifier for the user

Returns

  • Promise<boolean>: Resolves to true if SDK was started successfully, false otherwise

stopPoilabsAnalysis()

Stops the Poilabs Analysis SDK.

Returns

  • boolean: true if SDK was stopped successfully, false otherwise

updateUniqueId(uniqueId)

Updates the unique identifier in the SDK after initialization.

Parameters

  • uniqueId (String): New unique identifier for the user

Returns

  • Promise<boolean>: Resolves to true if update was successful

configureAnalysisSDK(options)

Configures the Poilabs Analysis SDK with advanced options including foreground service for doze mode prevention.

Parameters

  • options (Object):
    • enabled (boolean): Enable/disable the SDK
    • enableForegroundService (boolean): Enable foreground service to prevent doze mode (Android only)
    • serviceNotificationTitle (string): Title for the foreground service notification
    • notificationChannelName (string): Name for the notification channel
    • notificationChannelDescription (string): Description for the notification channel
    • notificationIconResourceId (number): Custom icon for the notification

Returns

  • Promise<boolean>: Resolves to true if configuration was successful

Example

// Enable foreground service for doze mode prevention
await configureAnalysisSDK({
  enabled: true,
  enableForegroundService: true,
  serviceNotificationTitle: 'Poilabs SDK Active',
  notificationChannelName: 'Poilabs SDK Service',
  notificationChannelDescription: 'Keeps Poilabs SDK running during doze mode'
});

requestRequiredPermissions()

Requests all the required permissions for the SDK to work properly.

Returns

  • Promise<boolean>: Resolves to true if all required permissions are granted, false otherwise

checkAllPermissions()

Checks if all required permissions are granted.

Returns

  • Promise<boolean>: true if all required permissions are granted, false otherwise

checkBluetoothPermission()

Checks if Bluetooth permissions are granted (relevant for Android 12+).

Returns

  • Promise<boolean>: true if Bluetooth permissions are granted, false otherwise

📋 Required Permissions

The plugin automatically adds these permissions:

Android

  • INTERNET - For network communication
  • ACCESS_FINE_LOCATION - For precise location
  • ACCESS_COARSE_LOCATION - For approximate location (Android 9 and below)
  • ACCESS_BACKGROUND_LOCATION - For background location tracking (Android 10+)
  • BLUETOOTH_CONNECT - For Bluetooth connectivity (Android 12+)
  • BLUETOOTH_SCAN - For Bluetooth scanning (Android 12+)
  • FOREGROUND_SERVICE with foregroundServiceType="location" - For background operations
  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - For preventing doze mode from stopping the SDK

iOS

  • NSLocationWhenInUseUsageDescription - Location permission when app is in use
  • NSLocationAlwaysUsageDescription - Location permission even when app is not in use
  • NSBluetoothAlwaysUsageDescription - Bluetooth permission

❓ Troubleshooting

Module not found error

If you see PoilabsAnalysisModule not found error:

  1. Make sure you have run npx expo prebuild
  2. Verify you've completed the additional setup steps for Android/iOS
  3. Run npx expo run:android or npx expo run:ios to build and run the native project
  4. For Expo Go, this plugin will not work because it requires native modules

iOS Integration Issues

If you're having issues with iOS integration:

  1. Make sure the Podfile is correctly updated with pod 'PoilabsAnalysis'
  2. Verify that use_frameworks! :linkage => :static is in your Podfile
  3. Check that the Swift files are properly added to your project
  4. Run pod install --repo-update from the ios directory

Permission issues

If the SDK is not working due to permission issues:

  1. Make sure you have requested all the necessary permissions
  2. For Android 10+, background location permission needs to be requested separately

🔧 For Poilabs Developers: SDK Version Management

This plugin uses centralized SDK version management through sdk-versions.json.

Setup Environment Variables

For automatic version fetching from private repositories, create a .env file:

# Copy the example file
cp env.example .env

# Edit with your tokens
# JITPACK_TOKEN=your_actual_jitpack_token_here

Note: The .env file is ignored by git for security.

Current SDK Versions

Check current versions:

npm run dev:show-versions

Updating SDK Versions

Manual version update:

npm run dev:update-sdk-versions -- --android v3.12.0
npm run dev:update-sdk-versions -- --ios 1.2.0
npm run dev:update-sdk-versions -- --android v3.12.0 --ios 1.2.0

Fetch latest versions automatically:

# Fetch latest versions for both platforms (uses .env file)
npm run dev:fetch-latest-versions

# Override with command line token
npm run dev:update-sdk-versions -- --fetch-latest --token YOUR_JITPACK_TOKEN

# Fetch latest for specific platform
npm run dev:update-sdk-versions -- --android latest
npm run dev:update-sdk-versions -- --ios latest

Release Process

  1. Test new SDK versions with current plugin
  2. Update versions using the script above
  3. Test thoroughly with updated versions
  4. Update plugin version in package.json
  5. Publish to npm: npm publish

CI/CD Integration

See .github/workflows/update-sdk.yml.example for automated SDK version management workflow.

📞 Support

If you encounter any issues, please contact Poilabs support or open an issue on GitHub.