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

@overlens/events-sdk

v0.1.0

Published

Browser SDK for Overlens Events: lead capture with eventId, UTMs and Meta fbp/fbc.

Readme

@overlens/events-sdk

SDK de navegador do Overlens Events para formulários de lead. Substitui o snippet copiado em cada landing page: gera e controla o eventId, captura UTMs, lê fbp/fbc da Meta e envia o lead para POST /webhooks/leads/:triggerSlug com keepalive, para que o envio sobreviva ao redirecionamento para o checkout.

  • Zero dependências de runtime.
  • ESM, CJS, tipos TypeScript e um build IIFE para páginas sem bundler.
  • submitLead nunca lança: qualquer problema vira { status: "failed" }, e a UX da LP não quebra por causa da SDK.
  • Seguro em SSR: importar o pacote e criar o cliente sem window não quebra (no servidor ele só não captura nada).

Instalação

Com bundler (npm)

pnpm add @overlens/events-sdk
import { createEventsClient } from "@overlens/events-sdk";

const events = createEventsClient({
  endpoint: "https://events.overlens.com.br",
  triggerSlug: "atlas-cam-0826",
});

form.addEventListener("submit", async (e) => {
  e.preventDefault();
  const result = await events.submitLead({
    email: form.email.value,
    name: form.name.value,        // opcional
    phone: form.phone.value,      // opcional
    honeypot: form.website.value, // opcional: o campo oculto anti-bot
  });
  // result.status: "accepted" | "unmatched" | "already_processed" | "failed"
});

Crie o cliente uma vez, no carregamento da página, e não dentro do submit. A captura de UTMs e do fbclid acontece na criação, enquanto a URL de entrada ainda tem a query string.

Sem bundler (<script> via jsDelivr)

<script src="https://cdn.jsdelivr.net/npm/@overlens/events-sdk@0/dist/index.global.js"></script>
<script>
  const events = OverlensEvents.createEventsClient({
    endpoint: "https://events.overlens.com.br",
    triggerSlug: "atlas-cam-0826",
  });
</script>

O build IIFE é minificado e expõe window.OverlensEvents. O @0 acompanha a versão 0.x mais recente. Para travar uma versão exata, use @0.1.0.

Opções de createEventsClient

| Opção | Tipo | Descrição | |---|---|---| | endpoint | string | Origem da API do Events. Barra final é tolerada. | | triggerSlug | string | Slug do gatilho lead_form. O POST vai para {endpoint}/webhooks/leads/{triggerSlug}. | | adapters | LeadAdapter[] | Destinos no navegador (Pixel, dataLayer) disparados com o mesmo eventId. Veja Adaptadores. | | fetch | typeof fetch | Opcional. O padrão é o fetch global. Serve para testes. |

submitLead(input)

| Campo | Tipo | Descrição | |---|---|---| | email | string | Obrigatório. | | name | string? | Vazio é omitido do envio (fluxo "veterano"). | | phone | string? | Vazio é omitido do envio. | | honeypot | string? | Valor do campo oculto website. Se vier preenchido, o lead é enviado normalmente (o servidor descarta em silêncio) e os adaptadores não são chamados. | | utms | Utms? | Substitui por inteiro as UTMs capturadas automaticamente ({ utmSource, utmMedium, utmCampaign, utmTerm, utmContent }). Útil na migração, para manter o readUtms() atual da LP. |

Resultado

Promise<{ status, eventId, error? }>, que sempre resolve:

| status | Significado | eventId seguinte | |---|---|---| | accepted | Lead recebido e encaminhado ao workflow. | novo | | unmatched | Lead recebido, mas ainda não existe workflow para o slug. Ele fica guardado e é reprocessado quando o gatilho for criado. | novo | | already_processed | Esse eventId já tinha sido recebido (duplo clique ou retry). | novo | | failed | Não foi possível confirmar o envio. error traz o motivo: o código da API (invalid_payload, invalid_origin, rate_limited…), http_<status>, network_error ou fetch_unavailable. | o mesmo: um novo submit deduplica no servidor |

O que a SDK faz sozinha

  • eventId: um UUID por sessão de formulário. Um duplo clique com o envio ainda em voo devolve a mesma Promise, então sai um único request. O id só muda depois de uma resposta confirmada (accepted, unmatched ou already_processed).
  • UTMs: lidas de location.search na criação do cliente, com last-touch não vazio. Uma URL com UTMs substitui as anteriores por inteiro, e uma URL sem UTMs mantém as que já estavam salvas. Ficam no localStorage sob a chave overlens-events:utms.
  • url: location.origin + location.pathname, sem a query string.
  • fbp: cookie _fbp. A SDK não cria esse cookie, porque ele pertence ao Pixel.
  • fbc: cookie _fbc. Sem ele, se a URL de entrada tiver fbclid, a SDK monta fb.1.{timestamp}.{fbclid} e guarda no sessionStorage (chave overlens-events:fbc). O cookie sempre tem precedência. A SDK não grava cookie.
  • Envio: POST com Content-Type: text/plain (dispensa o preflight de CORS) e keepalive: true. Falha de rede ou 5xx ganham até 2 novas tentativas com backoff curto (300 ms e 1 s) e o mesmo corpo. 4xx não tem retry.
  • Versão: todo corpo leva sdkVersion com a versão do pacote, embutida no build. Vai no corpo, e não num header, porque um header customizado exigiria o preflight de CORS. O dashboard usa esse campo para mostrar quais versões ainda recebem leads em cada origem (Configurações → "Versões da SDK nos últimos 30 dias").
  • Storage bloqueado (aba privada, cookies de terceiros): a SDK continua funcionando, só perde a persistência.

A origem da LP precisa estar na allowlist em /settings → Formulário de Lead do Events. Sem isso, a API responde 403 invalid_origin.

Adaptadores

Um adaptador dispara o lead num destino do navegador (o Pixel da Meta, o dataLayer do GTM) com o mesmo eventId do POST. É isso que permite à Meta contar o lead do Pixel e o da Conversions API como um só.

O cliente chama cada adaptador:

  • uma vez por eventId: duplo clique e retry depois de falha não disparam de novo;
  • de forma síncrona e antes do POST, porque a LP pode redirecionar para o checkout na linha seguinte;
  • isolado em try/catch: um adaptador que lança não afeta o lead nem os outros adaptadores;
  • nunca com o honeypot preenchido: bot não vira evento de mídia.
import { createEventsClient, dataLayer, metaPixel } from "@overlens/events-sdk";

const events = createEventsClient({
  endpoint: "https://events.overlens.com.br",
  triggerSlug: "atlas-cam-0826",
  adapters: [metaPixel()], // ou dataLayer({ ... }), veja abaixo
});

No build IIFE, eles ficam em OverlensEvents.metaPixel e OverlensEvents.dataLayer.

Use um caminho até o Pixel: metaPixel() quando o Pixel está no código da página, ou dataLayer() quando ele dispara por uma tag do GTM. Os dois juntos disparam o Lead duas vezes no navegador (a Meta desduplica pelo eventID, mas não há motivo para isso).

metaPixel(options?)

Chama o fbq que a página já tem, passando o eventId como eventID:

metaPixel();                                  // fbq("track", "Lead", {}, { eventID })
metaPixel({ eventName: "CompleteRegistration" }); // outro evento padrão → "track"
metaPixel({ eventName: "AtlasLead" });        // nome próprio → fbq("trackCustom", …)
metaPixel({ customData: () => ({ content_name: "Atlas" }) });

| Opção | Padrão | Descrição | |---|---|---| | eventName | "Lead" | Nome do evento no Pixel. Um evento padrão da Meta usa track, e qualquer outro nome usa trackCustom. Diferencia maiúsculas: lead é personalizado. Precisa ser o mesmo nome do nó Meta do workflow. | | customData | {} | (ctx) => objeto com o custom data do Pixel. Por padrão, nenhum dado pessoal é enviado. | | when | sempre | Consentimento, veja abaixo. |

Sem window.fbq na página, não faz nada. A SDK nunca injeta o Pixel: o Pixel, o id dele e o consentimento pertencem à LP.

dataLayer(options)

Faz o push no window.dataLayer (e o cria, se o GTM ainda não carregou) com o eventId em event_id:

dataLayer({
  event: "atlas_para_negocios_lead", // obrigatório: o mesmo nome que a LP já usa
  payload: (ctx) => ({ utm_source: ctx.page.utms.utmSource }),
});
// → dataLayer.push({ utm_source: "meta", event: "atlas_para_negocios_lead", event_id: "…" })

| Opção | Descrição | |---|---| | event | Obrigatório. A chave event que dispara o gatilho no GTM. | | payload | Opcional. (ctx) => objeto com as chaves extras do push. ctx traz eventId, lead (email, name?, phone?) e page (url, utms). | | when | Consentimento, veja abaixo. |

  • Nenhum dado pessoal vai por padrão. Só entra no push o que você colocar em payload.
  • event e event_id são aplicados depois do payload: uma chave do payload com o mesmo nome não consegue renomear o evento nem trocar o id.

Configuração no GTM (manual)

  1. Variáveis → Nova → Variável da camada de dados, com nome da variável event_id.
  2. Na tag do Pixel Lead (a que dispara no evento personalizado atlas_para_negocios_lead), preencha o campo Event ID com {{event_id}}. Em tags HTML personalizadas, passe { eventID: {{event_id}} } como quarto argumento do fbq("track", …).
  3. Publique o container depois do deploy da LP. Antes disso, event_id chega vazio: isso só perde o dedup, não quebra nada.

Migração de uma LP que já faz dataLayer.push

O adaptador passa a ser dono do push:

  1. Use o mesmo nome de evento de hoje em event. Não renomeie: há tags de mídia no container que dependem dele.
  2. Reproduza em payload todos os campos que o push atual manda. Se alguma tag do container (por exemplo, a tag HTML que envia para o Apps Script) lê e-mail, nome ou telefone do push, esses campos precisam estar no payload, porque o adaptador não envia nada pessoal por conta própria.
  3. Remova o dataLayer.push da LP. Com os dois ligados, o evento chega duas vezes ao GTM, e as tags disparam em dobro.
// antes, em pushLeadEvent:
window.dataLayer.push({ event: "atlas_para_negocios_lead", email, name, phone, ...utms });

// depois:
dataLayer({
  event: "atlas_para_negocios_lead",
  payload: ({ lead, page }) => ({ ...lead, ...page.utms }), // os mesmos campos de antes
});

Consentimento (when)

Todo adaptador aceita when?: () => boolean, ligado ao CMP da página. A função é consultada a cada lead, e não na criação do cliente, então um aceite dado depois do carregamento vale. Retornando false, o adaptador não dispara (e nem chama customData/payload). O lead continua sendo enviado para o Events. O padrão é sempre disparar.

metaPixel({ when: () => window.myCmp?.hasConsent("marketing") === true });

Adaptador próprio

import type { LeadAdapter } from "@overlens/events-sdk";

const logger: LeadAdapter = {
  name: "console",
  onLeadSubmitted({ eventId, lead, page }) {
    console.log("lead", eventId, lead.email, page.url, page.utms);
  },
};

onLeadSubmitted precisa ser síncrono. Trabalho assíncrono iniciado ali pode ser interrompido pela navegação da página.

FAQ: desduplicação com a Meta

Quando a Meta junta o evento do Pixel e o da Conversions API? Quando os dois chegam com o mesmo event_name e o mesmo event_id, com até 48 h de diferença. O event_id a SDK resolve: o adaptador e o POST usam o mesmo eventId, e o Events o repassa à CAPI. O nome é configurado em dois lugares, e só você consegue mantê-los iguais.

Onde fica o nome do lado do servidor? No nó Meta Ads do workflow do gatilho lead_form. Ele precisa usar exatamente o nome que o Pixel dispara: Lead (o padrão do metaPixel()) ou o eventName que você passou. O painel do nó mostra um aviso quando o gatilho é um Formulário de Lead.

E quando o Pixel dispara pelo GTM? Vale o nome da tag do Pixel no container (normalmente Lead), e não o event do dataLayer. O atlas_para_negocios_lead só aciona o gatilho do GTM. O event_id chega à tag pela variável de camada de dados event_id.

Como confirmo que funcionou? No Events Manager, em Test Events, um submit real mostra o evento do navegador e o do servidor com o mesmo event_id. Na visão geral do evento, os dois aparecem como desduplicados, sem dobrar a contagem.

O Pixel não está na página. Quebra algo? Não. Sem fbq, o metaPixel() não faz nada e o lead segue normalmente para a CAPI, só que sem par para desduplicar.

Releases

A SDK segue semver, com uma ressalva: enquanto estiver em 0.x, uma quebra de compatibilidade sobe o minor. Por isso, em produção, fixe a versão exata no jsDelivr (@0.1.0) ou use ^ com Renovate/Dependabot no npm. As mudanças de cada versão ficam no CHANGELOG.md, que vai junto no pacote publicado (a partir da primeira versão gerada pelo Changesets).

Para quem mantém a SDK (changesets, verificação no CI, publicação e fim de suporte de versões antigas): docs/ways-of-work/sdk-release.md.