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

react-native-qalink

v0.7.0

Published

Real-time error capture SDK for React Native — helps QA teams identify if bugs are frontend or backend

Readme

react-native-qalink 🔍

SDK de captura de errores en tiempo real para React Native. Captura errores, logs, eventos custom, requests HTTP y screenshots — permitiendo al equipo de QA ver en tiempo real qué está pasando en la app, sin necesidad de Android Studio ni logs técnicos.

La conexión WebSocket apunta siempre a wss://ws.airbites.org.


📦 Instalación

npm install react-native-qalink
# o
yarn add react-native-qalink

Dependencia opcional para capturas de pantalla:

npm install react-native-view-shot
cd ios && pod install

🚀 Inicio rápido

import QALink from 'react-native-qalink';

// 1. Inicializar el SDK
QALink.init({
  apiKey: 'qlk_your_api_key_here',
  appVersion: '1.0.0',
  environment: 'dev',
  debug: true,
});

// 2. Configurar contexto de usuario
QALink.setUserContext({
  userId: '12345',
  email: '[email protected]',
  plan: 'premium'
});

// 3. Configurar contexto custom
QALink.setCustomContext({
  experimentVariant: 'new_checkout_flow',
  featureFlags: { darkMode: true }
});

// 4. Usar los nuevos métodos de logging
QALink.debug('User opened settings');
QALink.info('Payment initiated', { amount: 99.99 });
QALink.warn('Slow API response', { duration: 3500 });
QALink.error('Payment failed', { code: 'CARD_DECLINED' });
QALink.critical('App about to crash');

// 5. Eventos custom
QALink.logEvent('checkout_completed', {
  items: 3,
  totalAmount: 299.99,
  paymentMethod: 'credit_card'
});

// 6. Captura de pantalla (requiere react-native-view-shot)
await QALink.captureScreen('before_payment');

🔧 Configuración HTTP avanzada

El SDK captura automáticamente todas las peticiones HTTP (fetch y axios). Puedes configurar qué capturar:

QALink.init({
  apiKey: 'qlk_xxx',
  appVersion: '1.0.0',
  httpConfig: {
    // Captura requests y responses
    captureRequests: true,
    captureResponses: true,
    
    // Filtrar por URL
    includeUrls: ['/api/payment', '/api/checkout'],
    excludeUrls: ['/api/analytics', '/health'],
    
    // Filtrar por método HTTP
    methods: ['POST', 'PUT', 'DELETE'], // ignorar GETs
    
    // Sanitización de datos sensibles
    sanitizeHeaders: ['Authorization', 'X-API-Key'],
    sanitizeRequestBody: ['password', 'creditCard', 'cvv'],
    sanitizeResponseBody: ['token', 'secret'],
    
    // Capturar solo errores HTTP (4xx, 5xx)
    captureOnlyErrors: true,
    minStatusCode: 400,
    
    // Límite de tamaño de body
    maxBodySize: 5000, // bytes
    
    // Performance monitoring
    captureTimings: true,
    slowRequestThreshold: 2000, // marcar requests > 2s como lentos
  }
});

Actualizar configuración HTTP en runtime:

QALink.configureHTTP({
  captureOnlyErrors: true,
  slowRequestThreshold: 1000
});

📝 API de Logging

Todos los métodos de logging hacen console.log nativo (visible en Metro) Y envían al servidor QALink:

// Acepta múltiples argumentos como console.log
QALink.debug('User action', { screen: 'Settings' }, 'Extra data');

QALink.info('API call started', {
  endpoint: '/api/users',
  method: 'GET'
});

QALink.warn('Memory warning', { 
  available: '50MB',
  threshold: '100MB' 
});

QALink.error('Validation failed', {
  field: 'email',
  reason: 'Invalid format'
});

QALink.critical('Unrecoverable error', {
  code: 'FATAL_001',
  message: 'Cannot connect to database'
});

Niveles de log:

  • debug → desarrollo y debugging
  • info → información general
  • warn → advertencias y problemas no críticos
  • error → errores recuperables
  • critical → errores fatales que pueden causar crash

🎯 Eventos Custom

Registra eventos de negocio importantes:

// Evento simple
QALink.logEvent('user_login');

// Evento con datos
QALink.logEvent('purchase_completed', {
  productId: 'SKU-123',
  price: 29.99,
  currency: 'USD',
  quantity: 2
});

QALink.logEvent('feature_used', {
  featureName: 'dark_mode',
  enabled: true,
  timestamp: Date.now()
});

Los eventos incluyen automáticamente:

  • User context (userId, email, etc.)
  • Custom context (feature flags, experiments)
  • Screen actual
  • Session ID
  • Device ID
  • Timestamp

👤 Context API

User Context

Información del usuario que se adjunta a todos los eventos:

QALink.setUserContext({
  userId: '12345',
  email: '[email protected]',
  username: 'john_doe',
  plan: 'premium',
  region: 'LATAM'
});

// Actualizar parcialmente
QALink.setUserContext({ plan: 'enterprise' });

// Limpiar
QALink.clearContext();

Custom Context

Datos custom que se adjuntan a todos los eventos:

QALink.setCustomContext({
  experimentVariant: 'new_ui_v2',
  featureFlags: {
    darkMode: true,
    newCheckout: false
  },
  build: 'staging-142'
});

📸 Captura de Pantalla

Requisito: react-native-view-shot

// Capturar pantalla completa
await QALink.captureScreen('before_payment');
await QALink.captureScreen('error_state');

// Capturar componente específico
import { useRef } from 'react';

const MyComponent = () => {
  const viewRef = useRef(null);
  
  const handleCapture = async () => {
    await QALink.captureRef(viewRef, 'form_error');
  };
  
  return (
    <View ref={viewRef}>
      {/* contenido */}
    </View>
  );
};

Configuración:

QALink.init({
  apiKey: 'qlk_xxx',
  appVersion: '1.0.0',
  enableScreenCapture: true, // ← activar
});

🔌 Interceptores HTTP

Fetch (automático)

El SDK intercepta todos los fetch() automáticamente. No necesitas hacer nada.

Axios

Si usas Axios, puedes interceptar instancias específicas:

import axios from 'axios';
import QALink from 'react-native-qalink';

const apiClient = axios.create({
  baseURL: 'https://api.example.com'
});

// Interceptar esta instancia de Axios
QALink.interceptAxios(apiClient);

🛠️ Configuración completa

import QALink from 'react-native-qalink';

QALink.init({
  // ── Requerido ──────────────────────────────────
  apiKey: 'qlk_your_64_char_hex_key',
  appVersion: '1.2.3',

  // ── Básico ─────────────────────────────────────
  enabled: true,              // habilitar/deshabilitar SDK
  environment: 'dev',         // 'dev' | 'prod'
  debug: true,                // logs de debug en consola

  // ── HTTP Interceptors ──────────────────────────
  httpConfig: {
    captureRequests: true,
    captureResponses: true,
    includeUrls: ['/api/'],
    excludeUrls: ['/health', '/metrics'],
    methods: ['POST', 'PUT', 'DELETE'],
    sanitizeHeaders: ['Authorization'],
    sanitizeRequestBody: ['password', 'token', 'creditCard'],
    sanitizeResponseBody: ['secret', 'apiKey'],
    captureOnlyErrors: false,
    minStatusCode: 200,
    maxBodySize: 5000,
    captureTimings: true,
    slowRequestThreshold: 2000,
  },

  // ── Legacy Network (compatibilidad) ────────────
  logNetworkBodies: false,
  sensitiveHeaders: ['Authorization'],
  sensitiveUrlPatterns: ['/auth/', '/login'],
  sensitiveBodyFields: ['password', 'token'],

  // ── Console ─────────────────────────────────────
  console: {
    captureLogs: true,
    captureWarnings: true,
    captureErrors: true,
    ignorePatterns: ['Ignore this'],
    includePatterns: [],
  },

  // ── Errors ──────────────────────────────────────
  captureRuntimeErrors: true,  // red screen / yellow box
  captureMetroErrors: true,    // bundler errors

  // ── Screen Capture ──────────────────────────────
  enableScreenCapture: true,   // requiere react-native-view-shot

  // ── Callbacks ───────────────────────────────────
  onEvent: (event) => {
    console.log('Event sent:', event.type);
  }
});

🧠 Advanced QA Features (v0.7.0)

Rage Click Detection

Detects when a user taps the same element multiple times rapidly — a strong frustration signal.

// 1. Enable (once, after init)
QALink.enableRageClickDetection({ threshold: 3, windowMs: 600 });

// 2. Call from any onPress handler
<Button
  onPress={() => {
    QALink.trackUIInteraction('checkout-submit-btn');
    handleSubmit();
  }}
/>

A rage_click event fires in the dashboard when the threshold is hit.


Navigation Loop Detection

Detects when the user bounces back and forth between the same two screens (A→B→A→B…). Signals broken back navigation or an unresolved error state.

// Enable (once, after init)
QALink.enableNavigationLoopDetection(3); // fires after 3 alternations

// Automatically triggered by setScreen() and startPackage()
QALink.setScreen('CheckoutScreen');

A navigation_loop event fires in the dashboard with the full navigation pattern.


Silent Failure Detection

The hardest QA bug: API returns 200 OK but the data is null/empty. No error, no crash — the app just silently breaks.

const orders = await fetchOrders(userId); // 200 OK

QALink.trackDataState({
  context: 'orders_list',
  expected: Array.isArray(orders) && orders.length > 0,
  actual: orders,
  statusCode: 200,
  url: '/api/orders',
});
// If orders is [] → fires a silent_failure event in the dashboard

Returns true if the check passes, false if a failure was detected.


Middleware (Event Processing Pipeline)

Intercept, enrich, filter, or transform any event before it's sent.

// Drop noisy console_log events in production
QALink.use((event, next) => {
  if (event.type === 'console_log') return; // drop
  next(event);
});

// Add a custom tag to every event
QALink.use((event, next) => {
  next({ ...event, _release: '2.1.0' } as typeof event);
});

// Log every event locally for debugging
QALink.use((event, next) => {
  console.log('[QALink middleware]', event.type);
  next(event);
});

Middlewares run in insertion order. Calling next is optional — skipping it drops the event.


Offline-First Queue

Events are buffered automatically when the connection drops. Auto-flush triggers:

QALink.init({
  apiKey: 'qlk_...',
  appVersion: '1.0.0',
  offlineQueue: {
    maxSize: 500,        // max buffered events (drops oldest when full)
    flushOnCount: 20,    // auto-flush when 20 events are buffered
    flushIntervalMs: 30_000, // auto-flush every 30s
  },
});

// Manual flush (e.g. before app goes to background)
QALink.flush();

If @react-native-async-storage/async-storage is installed, events also persist across app restarts.


Sequence Numbers

Every envelope sent to the dashboard now includes a sequenceNumber — a monotonically increasing counter per session. The dashboard can use it to detect out-of-order or missing events.


📦 Event Packages (v0.6.0)

Los Event Packages agrupan todos los eventos de una pantalla en un solo payload que se envía cuando el usuario sale de ella. Incluyen métricas pre-calculadas y el historial completo de eventos.

Integración en 1 línea — useTrackedScreen

import { useTrackedScreen } from 'react-native-qalink';

function CheckoutScreen() {
  useTrackedScreen('CheckoutScreen');
  // ↑ inicia el package al montar, lo cierra y envía al desmontar
  return <View>...</View>;
}

Eso es todo. Todos los eventos generados mientras el usuario está en esa pantalla (API calls, logs, errores, breadcrumbs) se agrupan y se envían juntos al salir.

API manual — startPackage / endPackage

// En React Navigation (App.tsx)
<NavigationContainer
  onStateChange={() => {
    const screen = navigationRef.current?.getCurrentRoute()?.name ?? '';

    QALink.endPackage();       // cierra el package de la pantalla anterior
    QALink.startPackage(screen); // inicia uno nuevo para la pantalla actual
  }}
>

Payload que recibe el dashboard

{
  "type": "event_package",
  "packageId": "...",
  "context": {
    "screenName": "CheckoutScreen",
    "sessionId": "...",
    "startTime": 1710445670000,
    "endTime":   1710445680000,
    "durationMs": 10000
  },
  "events": [ ...todos los eventos capturados en esa pantalla... ],
  "metrics": {
    "totalApiCalls":    3,
    "totalErrors":      1,
    "totalLogs":        5,
    "totalBreadcrumbs": 2,
    "totalEvents":     11
  }
}

Backward compatibility

Si no llamas startPackage ni useTrackedScreen, cada evento se sigue enviando inmediatamente como antes. Sin cambios de comportamiento.


📊 Tipos de eventos capturados

El SDK captura automáticamente:

| Tipo | Descripción | |------|-------------| | session_start | Inicio de sesión | | event_package | Batch de eventos de una pantalla (v0.6.0) | | rage_click | Usuario tapeó el mismo elemento N veces (v0.7.0) | | navigation_loop | Usuario rebotando entre mismas pantallas (v0.7.0) | | silent_failure | API 200 OK con datos inválidos/vacíos (v0.7.0) | | http_request | Request HTTP (fetch/axios) | | user_log | Logs con debug/info/warn/error/critical | | custom_event | Eventos custom con logEvent() | | console_log | console.log | | console_warn | console.warn | | console_error | console.error | | runtime_error | Red screen / Yellow box | | metro_error | Errores de Metro Bundler | | js_error | Errores de JavaScript | | breadcrumb | Navegación y acciones | | screen_capture | Screenshots |


🔐 Compatibilidad con fetch-blob

El interceptor de fetch es 100% compatible con fetch-blob:

import { fetch, Blob } from 'react-native-fetch-blob';

// QALink detecta automáticamente Blobs y no intenta serializarlos
const blob = new Blob([...], { type: 'image/jpeg' });

await fetch('https://api.example.com/upload', {
  method: 'POST',
  body: blob
});

// En el dashboard verás:
// body: { _type: 'Blob', size: 12345, type: 'image/jpeg' }

🎯 Casos de uso

1. Tracking de flujo de checkout

QALink.setUserContext({ userId: user.id, email: user.email });

QALink.logEvent('checkout_started', { cartTotal: 299.99 });
QALink.info('Loading payment methods');

// ... usuario selecciona método de pago ...
QALink.logEvent('payment_method_selected', { method: 'credit_card' });

try {
  const response = await processPayment();
  QALink.logEvent('payment_success', { 
    orderId: response.orderId,
    amount: 299.99 
  });
} catch (error) {
  QALink.error('Payment failed', { 
    reason: error.message,
    code: error.code 
  });
  await QALink.captureScreen('payment_error');
}

2. Debugging de API lenta

QALink.configureHTTP({
  captureTimings: true,
  slowRequestThreshold: 1500, // 1.5s
});

// Los requests > 1.5s aparecerán marcados en el dashboard

3. Tracking de experimentos A/B

const variant = getExperimentVariant('new_ui');

QALink.setCustomContext({
  experiment: 'new_ui',
  variant: variant, // 'A' or 'B'
});

QALink.logEvent('experiment_viewed', { 
  experiment: 'new_ui',
  variant 
});

4. Event Package con React Navigation

// App.tsx
import { useTrackedScreen } from 'react-native-qalink';

// En cada pantalla — 1 sola línea:
function PaymentScreen() {
  useTrackedScreen('PaymentScreen');
  // ...
}

function ProfileScreen() {
  useTrackedScreen('ProfileScreen');
  // ...
}

O con NavigationContainer para cobertura automática de todas las pantallas:

// App.tsx
import { NavigationContainer } from '@react-navigation/native';
import { QALink } from 'react-native-qalink';

<NavigationContainer
  onStateChange={() => {
    const screen = navigationRef.current?.getCurrentRoute()?.name ?? 'unknown';
    QALink.endPackage();        // cierra el package de la pantalla anterior
    QALink.startPackage(screen); // inicia uno nuevo
  }}
>

📋 Referencia completa de la API

| Método | Descripción | |--------|-------------| | init(config) | Inicializa el SDK. Requerido antes de todo. | | interceptAxios(instance) | Registra una instancia de Axios. | | use(middleware) | Agrega un middleware al pipeline de eventos. | | setUserContext(ctx) | Adjunta datos del usuario a todos los eventos. | | setCustomContext(ctx) | Adjunta datos custom a todos los eventos. | | clearContext() | Limpia usuario y contexto custom. | | debug(...args) | Log nivel debug. | | info(...args) | Log nivel info. | | warn(...args) | Log nivel warn. | | error(...args) | Log nivel error. | | critical(...args) | Log nivel critical. | | logEvent(name, data?) | Evento custom de negocio. | | addBreadcrumb(action, data?) | Breadcrumb manual. | | logRequest(options) | Registra request de red manualmente. | | setScreen(name) | Cambia la pantalla actual + breadcrumb + loop detection. | | startPackage(screenName) | Inicia un Event Package para esa pantalla. | | endPackage() | Cierra el package activo y lo envía. | | flush() | Envía inmediatamente todos los eventos en cola offline. | | enableRageClickDetection(options?) | Activa detección de rage clicks. | | trackUIInteraction(target) | Registra tap para rage click. | | enableNavigationLoopDetection(threshold?) | Activa detección de loops de navegación. | | trackDataState(check) | Detecta silent failures (API 200 con datos inválidos). | | captureScreen(label?) | Captura la pantalla completa. | | captureRef(ref, label?) | Captura un componente específico. | | configureHTTP(config) | Actualiza config HTTP en runtime. | | getDeviceId() | Devuelve el deviceId de la sesión. | | getStatus() | Estado de la conexión WebSocket. | | getQueueSize() | Número de eventos en cola offline. | | destroy() | Limpia interceptores y desconecta. |

Hook:

| Hook | Descripción | |------|-------------| | useTrackedScreen(name) | 1 línea: inicia y cierra el package automáticamente al montar/desmontar la pantalla. |


🚨 Troubleshooting

Los logs no aparecen en el dashboard

  • Verifica que apiKey sea correcto (formato: qlk_<64 hex>)
  • Verifica que enabled: true
  • Revisa la consola: QALink.getStatus() debe ser 'connected'

Screen capture no funciona

  • Instala: npm install react-native-view-shot
  • Habilita: enableScreenCapture: true en config
  • iOS: cd ios && pod install

Axios no se intercepta

  • Llama QALink.interceptAxios(axiosInstance) después de QALink.init()

📖 Recursos

  • Dashboard: https://qalink.airbites.org
  • Documentación completa: (próximamente)
  • GitHub: (próximamente)

📄 Licencia

MIT


🤝 Contribuir

Pull requests son bienvenidos. Para cambios mayores, abre primero un issue.