npm package: @instalog.dev/expo
Description: A React Native module for integrating Instalog analytics and logging into Expo applications
Author: Chiziaruhoma Ogbonda
Latest version: 0.1.0

# Instalog Expo

An Expo module wrapper for the [Instalog](https://instalog.dev) analytics and logging SDK, making it easy to integrate Instalog into your Expo applications.

## Installation

```bash
npm install @instalog.dev/expo
```

### Expo Config Plugin

> This package auto-installs necessary native dependencies for Expo projects.

### Android Configuration

For Android, make sure to add the Instalog Feedback Activity to your AndroidManifest.xml:

```xml
<activity
  android:name="dev.instalog.mobile.feedback.InstalogFeedbackActivity"
  android:label="Instalog"
  android:theme="@style/Theme.Instalog" />
```

This should be placed inside the `<application>` tags of your manifest file.

## Usage

### Initialization

Initialize the Instalog SDK at the beginning of your app:

```javascript
import { Instalog } from '@instalog.dev/expo';

// Initialize with default settings
await Instalog.initialize('YOUR_API_KEY');

// Or with custom options
await Instalog.initialize('YOUR_API_KEY', {
  isLogEnabled: true,
  isCrashEnabled: true,
  isFeedbackEnabled: true,
  isLoggerEnabled: false, // Debug logging
  autoCaptureCrashes: true, // Automatically capture unhandled JS errors
  errorGrouping: {
    enabled: true,
    flushIntervalMs: 30000, // 30 seconds
    errorThreshold: 5,
    timeWindowMs: 60000, // 1 minute
    flushOnBackground: true,
    requireNetwork: true,
    maxErrorsPerGroup: 10,
    dynamicFlushInterval: true,
  }
});

// Identify your user after initialization
await Instalog.identifyUser("user123");
```

### User Identification

Identify your users to track their activity across sessions:

```javascript
await Instalog.identifyUser('user123');
```

### Event Logging

Track user actions and events in your app:

```javascript
await Instalog.logEvent('button_clicked', {
  button_name: 'submit',
  screen: 'login',
});
```

### Feedback Collection

Show a feedback modal to collect user feedback:

```javascript
await Instalog.showFeedbackModal();
```

### Crash Reporting

Crash reporting is automatically enabled after initialization. The SDK can automatically capture unhandled JavaScript errors when `autoCaptureCrashes` is enabled (default).

You can also manually report crashes:

```javascript
try {
  // Your code that might throw an exception
} catch (error) {
  await Instalog.sendCrash('Error name', error.stack || error.toString());
}
```

For more structured error reporting, you can use the `formatErrorReport` helper:

```javascript
try {
  // Your code that might throw an exception
} catch (error) {
  const formattedError = Instalog.formatErrorReport(error, errorInfo);
  await Instalog.sendCrash('Caught Error', formattedError);
}
```

For testing, you can simulate a crash:

```javascript
await Instalog.simulateCrash();
```

### Global Error Handling

The SDK automatically sets up global error handling when initialized with `autoCaptureCrashes: true` (default). This captures unhandled JavaScript errors and reports them to Instalog.

You can test this functionality with:

```javascript
// This will trigger the global error handler
setTimeout(() => {
  throw new Error("Test Global Error");
}, 0);
```

## API Reference

### `Instalog.initialize(key: string, options?: InstalogOptions): Promise<boolean>`

Initializes the SDK with your API key and optional configuration.

**Options:**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| isLogEnabled | boolean | false | Enable event logging |
| isLoggerEnabled | boolean | false | Enable debug logging |
| isCrashEnabled | boolean | false | Enable crash reporting |
| isFeedbackEnabled | boolean | false | Enable feedback collection |
| autoCaptureCrashes | boolean | true | Automatically capture unhandled JS errors |
| errorGrouping | object | (see below) | Configure how errors are grouped and reported |

**Error Grouping Options:**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | boolean | true | Enable error grouping |
| flushIntervalMs | number | 30000 | Interval to flush error groups (ms) |
| errorThreshold | number | 5 | Number of errors to trigger flush |
| timeWindowMs | number | 60000 | Time window for grouping errors (ms) |
| flushOnBackground | boolean | true | Flush errors when app goes to background |
| requireNetwork | boolean | true | Only flush when network is available |
| maxErrorsPerGroup | number | 10 | Max errors to store per group |
| dynamicFlushInterval | boolean | true | Adjust flush interval based on error volume |

### `Instalog.identifyUser(userId: string): Promise<boolean>`

Identifies the current user.

### `Instalog.logEvent(event: string, params?: Record<string, any>): Promise<boolean>`

Logs an event with optional parameters.

### `Instalog.showFeedbackModal(): Promise<boolean>`

Shows the feedback collection modal.

### `Instalog.sendCrash(error: string, stack: string): Promise<boolean>`

Manually sends a crash report.

### `Instalog.formatErrorReport(error: Error, errorInfo?: any): string`

Formats an error into a structured report for sending.

### `Instalog.simulateCrash(): Promise<boolean>`

Simulates a crash for testing.

## Example Application

```javascript
import { Instalog } from "@instalog.dev/expo";
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, View, TouchableOpacity, SafeAreaView } from "react-native";

export default function App() {
  const apiKey = "YOUR_API_KEY";

  useEffect(() => {
    const initialize = async () => {
      await Instalog.initialize(apiKey, {
        isLogEnabled: true,
        isCrashEnabled: true,
        isFeedbackEnabled: true,
        isLoggerEnabled: true,
      });
      await Instalog.identifyUser("expo_test_user");
    };
    initialize();
  }, []);

  const handleSendEvent = async () => {
    await Instalog.logEvent("button_clicked", {
      timestamp: new Date().toISOString(),
      screen: "demo_screen",
    });
  };

  const handleShowFeedback = async () => {
    await Instalog.showFeedbackModal();
  };

  const handleSimulateCrash = async () => {
    await Instalog.simulateCrash();
  };

  const handleTestGlobalError = () => {
    setTimeout(() => {
      throw new Error("Test Global Error");
    }, 0);
  };

  const handleManualErrorReport = async () => {
    await Instalog.sendCrash(
      "Manual Test Error",
      "This is a stack trace for a manually reported error\nLine 1\nLine 2\nLine 3"
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.buttonContainer}>
        <TouchableOpacity
          style={styles.button}
          onPress={handleSendEvent}
        >
          <Text style={styles.buttonText}>Send Event</Text>
        </TouchableOpacity>
        
        <TouchableOpacity
          style={styles.button}
          onPress={handleShowFeedback}
        >
          <Text style={styles.buttonText}>Show Feedback</Text>
        </TouchableOpacity>
        
        <TouchableOpacity
          style={styles.button}
          onPress={handleSimulateCrash}
        >
          <Text style={styles.buttonText}>Simulate Crash</Text>
        </TouchableOpacity>
        
        <TouchableOpacity
          style={styles.button}
          onPress={handleTestGlobalError}
        >
          <Text style={styles.buttonText}>Test Global Error</Text>
        </TouchableOpacity>
        
        <TouchableOpacity
          style={styles.button}
          onPress={handleManualErrorReport}
        >
          <Text style={styles.buttonText}>Manual Error Report</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    backgroundColor: "#fff",
  },
  buttonContainer: {
    alignItems: "center",
  },
  button: {
    backgroundColor: "#8860E6",
    padding: 15,
    borderRadius: 8,
    marginVertical: 10,
    width: 200,
    alignItems: "center",
  },
  buttonText: {
    color: "#fff",
    fontWeight: "bold",
  },
});
```

## License

MIT