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

bridge-analytics

v1.1.0

Published

Lightweight TypeScript client for Bridge BaaS analytics (POST /analytics/track). Works in React Native (fetch).

Downloads

242

Readme

bridge-analytics

Cliente TypeScript leve para o endpoint de analytics do Bridge BaaS (POST /v1/analytics/track). É um wrapper em volta de fetch: sem dependências em runtime, adequado para Node 18+, browser e React Native.

Além do nome do evento e do userId opcional, podes enviar metadata: um objeto JSON dinâmico por evento (modelo de IA, ecrã, SKU, duração, etc.) — a lib serializa e envia tal como o Bridge espera.

Índice

Instalação

npm install bridge-analytics
yarn add bridge-analytics
pnpm add bridge-analytics

Conceitos (Bridge)

O Bridge identifica a tua aplicação pelo header x-api-key (a “API key” da app no painel). Cada pedido de analytics fica isolado por app (multitenancy).

No corpo do evento o Bridge aceita:

| Campo | Obrigatório | Descrição | |------------|-------------|-----------| | name | Sim | Nome do evento (ex.: app_open, image_generated). | | userId | Não | Se enviado, tem de ser um User.id válido desta app; IDs inválidos ou de outra app → 400. Sem userId, o evento pode ser anónimo. | | metadata | Não | Objeto JSON arbitrário (strings, números, booleanos, objetos/arrays aninhados). O Bridge limita tamanho (~8KB) e profundidade no servidor. Útil para modelo, ecrã, créditos gastos, etc. |

Enviar userId em eventos como “abertura da app” ajuda o Bridge a saber a última atividade do utilizador (útil para campanhas com inactiveOnlyDays em push/broadcast, conforme a documentação do teu BaaS).

Esta biblioteca não envia appId no body: o servidor deriva o contexto pela API key. O campo appId na configuração do cliente é opcional e serve só para o teu código (logs, multi-app no cliente).

Metadados dinâmicos (metadata)

A partir da API atual do Bridge, cada evento pode incluir um campo opcional metadata: qualquer objeto JSON que queiras associar ao evento, sem schema fixo na lib. Tu defines as chaves em runtime conforme a app (ex.: model, screen, creditsSpent, predictionId).

O que podes enviar

| Tipo no metadata | Exemplo | |--------------------|---------| | string | "model": "google/gemini-3-flash" | | number | "credits": 5 | | boolean | "isPremium": true | | null | "error": null | | Objetos aninhados | "context": { "screen": "editor", "tab": "export" } | | Arrays | "tags": ["ai", "image"] |

A lib tipa isto com BridgeAnalyticsMetadata / BridgeAnalyticsMetadataValue em TypeScript. O Bridge persiste o JSON no servidor; no painel /dashboard/analytics vês os eventos com o contexto que enviaste.

Limites (servidor Bridge)

  • Tamanho aproximado ~8 KB por pedido de metadata.
  • Profundidade de objetos aninhados limitada no servidor.
  • Se excederes, o Bridge responde 400 — trata com BridgeAnalyticsError.

Como enviar na lib

Forma 1 — terceiro argumento (igual ao SDK mínimo do Bridge):

await analytics.track('image_generated', user.id, {
  model: 'wan-video/wan-2.7-image-pro',
  generatedAt: new Date().toISOString(),
});

Forma 2 — objeto completo (recomendado quando só queres metadata, sem userId):

await analytics.track({
  name: 'screen_view',
  metadata: { screen: 'paywall', source: 'deep_link' },
});

Forma 3 — evento identificado + metadados ricos:

await analytics.track({
  name: 'ai_run_complete',
  userId: user.id,
  metadata: {
    model: 'google/nano-banana-2',
    outputKind: 'IMAGE',
    durationMs: 4200,
    private: true,
  },
});

metadata é sempre opcional. Se não passares, o body fica só { "name": "..." } (e userId se existir) — como antes, para pings anónimos ou simples.

Exemplos de uso

| Cenário | name | metadata sugerido | |---------|--------|---------------------| | Geração de imagem | image_generated | { model, generatedAt } | | Paywall | purchase_tap | { screen, sku, tier } | | Navegação | screen_view | { screen, previousScreen } | | Erro de fluxo | checkout_failed | { step, errorCode } |

React Native (fire-and-forget com metadata)

analytics
  .track('app_open', user.id, { build: '1.2.0', platform: 'ios' })
  .catch((err) => console.warn('[analytics]', err));

Início rápido

import { createBridgeAnalytics } from 'bridge-analytics';

const analytics = createBridgeAnalytics({
  apiKey: 'sua_chave_da_app',
});

// Utilizador identificado (userId Bridge)
await analytics.track('app_open', user.id);

// Só nome do evento (sem userId no body)
await analytics.track({ name: 'screen_settings' });

// Evento com metadados dinâmicos
await analytics.track('image_generated', user.id, {
  model: 'wan-video/wan-2.7-image-pro',
  generatedAt: new Date().toISOString(),
});

await analytics.track({
  name: 'purchase_tap',
  metadata: { screen: 'paywall', sku: 'com.app.premium.month' },
});

Configuração (createBridgeAnalytics)

import { createBridgeAnalytics } from 'bridge-analytics';

const analytics = createBridgeAnalytics({
  apiKey: string;        // obrigatório → header x-api-key
  appId?: string;        // opcional, não vai no POST
  baseUrl?: string;      // opcional; ver secção self-hosted
  fetch?: typeof fetch;  // opcional; para testes ou polyfill
});

| Opção | Tipo | Descrição | |-----------|----------|-----------| | apiKey | string | Chave da app no Bridge. Enviada em cada pedido como x-api-key. | | appId | string | Opcional. Exposto em analytics.appId para referência no cliente; não é enviado no body de /analytics/track. | | baseUrl | string | Raiz da API com sufixo /v1. | | fetch | fetch | Opcional. Por omissão usa globalThis.fetch. |

O cliente devolvido inclui:

  • appId — o valor que passaste em config.appId, ou undefined.
  • track — ver abaixo.

Enviar eventos (track)

Cada chamada envia name (obrigatório), userId (opcional) e metadata (opcional). Ver Metadados dinâmicos para detalhes e exemplos.

Duas formas de chamar:

// 1) Nome + userId + metadata opcionais (mesma ordem do SDK Bridge)
await analytics.track('clicked_buy_button', userId);
await analytics.track('image_generated', userId, { model: 'google/gemini-3-flash' });

// Metadados sem userId (passa undefined no userId)
await analytics.track('screen_view', undefined, { screen: 'home' });

// 2) Objeto (igual ao body JSON do Bridge)
await analytics.track({ name: 'clicked_buy_button', userId });
await analytics.track({
  name: 'image_generated',
  userId,
  metadata: { model: 'wan-video/wan-2.7-image-pro' },
});
await analytics.track({ name: 'anonymous_ping' }); // sem userId nem metadata

Regras:

  • name é obrigatório e tem de ser uma string não vazia; caso contrário a lib lança TypeError antes do pedido HTTP.
  • userId / metadata omitidos ou undefined → essas chaves não entram no JSON (variante com argumentos posicionais).
  • metadata só é enviado quando definido; podes usar objeto aninhado conforme a API Bridge (respeita os limites do servidor).

O pedido HTTP é:

  • Método: POST
  • URL: {baseUrl}/analytics/track
  • Headers: Content-Type: application/json, x-api-key: <apiKey>
  • Body: JSON.stringify({ name, userId?, metadata? })

Comportamento assíncrono

track devolve uma Promise<void>: o trabalho de rede é assíncrono e não bloqueia a UI do React Native enquanto o fetch decorre.

Podes disparar sem await (fire-and-forget); o evento é enviado desde que o fetch arranque. Recomenda-se tratar falhas para evitar unhandled rejection:

analytics.track('app_open', user.id).catch((err) => {
  console.warn('[bridge-analytics]', err);
});

Erros

BridgeAnalyticsError

Para respostas HTTP não OK (4xx/5xx), a lib lança BridgeAnalyticsError:

| Propriedade | Tipo | Descrição | |------------|-----------|-----------| | message | string | Mensagem; preferencialmente o campo error do JSON do Bridge, se existir. | | status | number | Código HTTP (ex.: 400, 429). | | body | unknown | Corpo parseado como JSON ou texto bruto. |

import { BridgeAnalyticsError } from 'bridge-analytics';

try {
  await analytics.track('x', 'user_invalido');
} catch (e) {
  if (e instanceof BridgeAnalyticsError) {
    console.error(e.status, e.body);
  }
  throw e;
}

Payload de erro da API (BridgeApiErrorPayload)

O Bridge costuma responder com algo como:

{ "error": "Human readable message", "details": "..." }

O tipo exportado BridgeApiErrorPayload reflete error e details opcionais para tipares o body quando fizer sentido.

fetch indisponível

Se não existir fetch global e não passares fetch na config, a lib lança Error com uma mensagem a indicar que deves injectar fetch ou usar um runtime com fetch.

React Native

  • Usa apenas fetch e JSON: compatível com o ambiente JS do Metro/Hermes em versões recentes.
  • Não uses credenciais hardcoded: preferência por variáveis de ambiente no build (ex. EXPO_PUBLIC_*, react-native-config, etc.) ou um segredo injetado em CI.

Exemplo com metadados dinâmicos:

import { Platform } from 'react-native';
import { createBridgeAnalytics } from 'bridge-analytics';

export const analytics = createBridgeAnalytics({
  apiKey: process.env.EXPO_PUBLIC_BRIDGE_API_KEY!,
  appId: process.env.EXPO_PUBLIC_BRIDGE_APP_ID,
});

// Abertura da app + contexto da build
analytics.track('app_open', userId, {
  version: '1.2.0',
  platform: Platform.OS,
}).catch(console.warn);

Instância self-hosted

Se o teu Bridge estiver noutro domínio, define baseUrl com a raiz da API incluindo /v1:

const analytics = createBridgeAnalytics({
  apiKey: '...',
  baseUrl: 'https://api.teu-dominio.com/v1',
});

Barra final em baseUrl é normalizada (removida) antes de concatenar /analytics/track.

Referência de tipos (TypeScript)

Exportações principais:

| Símbolo | Descrição | |---------|-----------| | createBridgeAnalytics | Fábrica do cliente (também default export). | | BRIDGE_ANALYTICS_DEFAULT_BASE_URL | Constante da URL base pública do Bridge (…/v1). | | BridgeAnalyticsClient | Tipo do objeto { appId, track }. | | BridgeAnalyticsClientConfig | Opções do createBridgeAnalytics. | | BridgeAnalyticsTrackBody | Forma do body { name, userId?, metadata? }. | | BridgeAnalyticsMetadata | Tipo dos metadados dinâmicos (JSON serializável). | | BridgeAnalyticsMetadataValue | Valores permitidos dentro de metadata. | | BridgeAnalyticsError | Erro lançado em falhas HTTP. | | BridgeApiErrorPayload | Forma típica do JSON de erro da API. |

import createBridgeAnalytics, {
  BRIDGE_ANALYTICS_DEFAULT_BASE_URL,
  type BridgeAnalyticsClient,
  BridgeAnalyticsError,
} from 'bridge-analytics';

Licença

MIT — ver ficheiro LICENSE.