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

@neilton2026/stealth-client

v1.0.2

Published

HTTP client stealth com TLS, HTTP/2, fingerprint e Akamai solver

Readme

StealthClient — Documentação Completa

Cliente HTTP stealth em TypeScript que impersona um browser Chrome real para bypassar detecções de bot (Cloudflare, Akamai, Datadome, Shape Security).


Arquitetura — 3 Camadas

StealthClient
├── fingerprint-generator (Apify)   → Headers + fingerprint browser coerentes (rede Bayesiana)
├── impit (Rust / napi-rs)          → TLS JA3/JA4 + HTTP/2 Akamai frame order idênticos ao Chrome
└── akamai-v3-sensor-data-helper    → Resolve JS challenge do Akamai sem browser real

Por que cada camada existe

| Camada | O que resolve | |--------|---------------| | fingerprint-generator | Garante que UA, sec-ch-ua, screen, WebGL e navigator são internamente consistentes — bot detectors cruzam esses valores | | impit | TLS handshake idêntico ao Chrome real (JA3/JA4). Node.js padrão usa OpenSSL que é detectado por Cloudflare, Akamai etc. | | akamai-v3-sensor-data-helper | Sites com Akamai Bot Manager exigem sensor_data gerado por JS executado no browser. Esta lib faz o reverse do algoritmo |


Estrutura de Arquivos

stealth-client/
├── src/
│   ├── stealth-client.ts          # Classe principal — ponto de entrada
│   ├── types/index.ts             # Todos os tipos TypeScript
│   ├── core/
│   │   ├── tls-client.ts          # Wrapper do impit (TLS/HTTP2)
│   │   └── session.ts             # Gerenciador de cookies e perfil de sessão
│   ├── profiles/
│   │   └── browser-profiles.ts   # Geração de perfil + headers Chrome/Firefox
│   └── solvers/
│       └── akamai.ts              # Solver do Akamai sensor_data
├── examples/
│   ├── fingerprint-test.ts        # Testa fingerprint completo (TLS, headers, Cloudflare)
│   └── usage.ts                   # Exemplo de uso básico
├── package.json
└── tsconfig.json

Instalação

# Node.js >= 20 obrigatório
cd stealth-client
npm install

Dependências principais:

| Pacote | Versão | Função | |--------|--------|--------| | impit | ^0.8.0 | TLS fingerprint (Rust nativo) | | fingerprint-generator | ^2.1.81 | Geração de fingerprint coerente | | akamai-v3-sensor-data-helper | ^1.0.0 | Resolver Akamai JS challenge | | tsx | ^4.19.0 | Roda TypeScript direto sem build |


Uso Básico

import { StealthClient } from '../src/stealth-client.js';

const client = new StealthClient({
  browser: 'chrome',   // 'chrome' | 'firefox' | 'safari' | 'edge'
  os: 'windows',       // 'windows' | 'macos' | 'linux' | 'android' | 'ios'
  locale: 'en-US',     // idioma dos headers Accept-Language
});

// GET simples
const res = await client.get('https://site.com/pagina');
console.log(res.status, res.body);

// POST JSON
const post = await client.post('https://site.com/api', {
  campo1: 'valor1',
  campo2: 'valor2',
});

// POST com corpo customizado e headers extras
const post2 = await client.post('https://site.com/api', { data: 'x' }, {
  headers: { 'X-Custom': 'valor' },
  timeout: 15_000,
});

Opções do Construtor (StealthClientOptions)

interface StealthClientOptions {
  browser?: 'chrome' | 'firefox' | 'safari' | 'edge';  // default: 'chrome'
  os?: 'windows' | 'macos' | 'linux' | 'android' | 'ios'; // default: 'windows'
  locale?: string;           // default: 'pt-BR'  (ex: 'en-US', 'es-MX')
  proxyUrl?: string;         // ex: 'http://user:pass@host:port'
  timeout?: number;          // default: 30000 (ms)
  solveAkamai?: boolean;     // default: true — resolve sensor_data automaticamente
  followRedirects?: boolean; // default: true
}

Opções de Request (RequestOptions)

interface RequestOptions {
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
  headers?: Record<string, string>;  // headers adicionais (mesclados com os do perfil)
  body?: string | URLSearchParams | Record<string, unknown>;
  timeout?: number;
  followRedirects?: boolean;
}

Resposta (StealthResponse)

interface StealthResponse {
  status: number;                     // HTTP status code
  statusText: string;                 // ex: 'OK', 'Forbidden'
  headers: Record<string, string>;    // headers da resposta (lowercase)
  cookies: Cookie[];                  // cookies recebidos nesse request
  body: string;                       // corpo da resposta como string
  elapsed: number;                    // tempo do request em ms
  url: string;                        // URL final (após redirects)
  json<T>(): T;                       // parse do body como JSON (lança StealthError se inválido)
}

Headers Gerados Automaticamente

GET (navegação principal)

sec-ch-ua: "Not:A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... Chrome/145.0.0.0 ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,...
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US
Priority: u=0, i
Cookie: <cookies automáticos da sessão>

POST / AJAX (XHR interno da página)

sec-ch-ua: "Not:A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/145.0.0.0 ...
Content-Type: application/json
Accept: */*
Origin: https://dominio-do-site.com
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,pt;q=0.9,...
Referer: <URL passada como referer>
Cookie: <cookies automáticos da sessão>

IMPORTANTE: Nunca adicione User-Agent, sec-ch-ua, Sec-Fetch-* manualmente. O cliente já gera tudo consistente. Só adicione headers extras específicos do site (ex: X-Requested-With, tokens customizados).


Fingerprint Gerado (Browser Profile)

O fingerprint-generator (Apify) usa uma rede Bayesiana treinada em dados reais de browsers. Cada execução gera um perfil diferente mas sempre internamente consistente:

const profile = client.getProfile();

profile.fingerprint.userAgent        // 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/145 ...'
profile.fingerprint.screen           // { width: 1920, height: 1080, colorDepth: 32, pixelDepth: 32, devicePixelRatio: 1 }
profile.fingerprint.navigator        // { hardwareConcurrency: 16, deviceMemory: 8, platform: 'Win32', ... }
profile.fingerprint.webGl            // { vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA ...)', ... }
profile.fingerprint.fonts            // ['Arial', 'Calibri', 'Times New Roman', ...]
profile.meta.browserVersion          // '145.0.0.0'
profile.meta.os                      // 'windows'

TLS e HTTP/2 Fingerprint

O impit (binding Rust via napi-rs) substitui o stack TLS padrão do Node.js:

| Campo | Valor | Significado | |-------|-------|-------------| | JA4 | t13d1516h2_8daaf6152771_02713d6af862 | Chrome real no Windows | | HTTP | http/2 | HTTP/2 com frame order do Chrome | | TLS | TLSv1.3 | TLS 1.3 obrigatório | | kex | X25519MLKEM768 | Key exchange do Chrome 130+ |

Edge/Safari são mapeados internamente para o fingerprint TLS do Chrome. O impit só suporta chrome e firefox como perfis TLS nativos.


Gerenciamento de Cookies

O SessionManager persiste cookies automaticamente entre requests — igual a um browser real:

// Cookies são coletados automaticamente em cada resposta
const res = await client.get('https://site.com');
// Próximo request já envia os cookies recebidos

// Injetar cookies manualmente (ex: sessão exportada do browser)
client.addCookies([
  {
    name: '__cf_bm',
    value: 'abc123...',
    domain: 'site.com',
    path: '/',
    secure: true,
    httpOnly: true,
  }
]);

// Ver todos os cookies da sessão
const stats = client.getSessionStats();
console.log(stats.cookieCount, stats.requestCount, stats.ageMs);

Usando Proxy

// Proxy HTTP com autenticação
const client = new StealthClient({
  proxyUrl: 'http://usuario:senha@host:porta',
});

// Proxy residencial (formato comum)
const client2 = new StealthClient({
  proxyUrl: 'http://bp-sqsp1cakhi6w_area-US:[email protected]:2312',
});

Atenção: O impit tem problemas conhecidos com proxies HTTP autenticados em algumas versões. Se der TunnelUnsuccessful, tente sem proxy primeiro para isolar o problema.


Akamai Bot Manager

O solver do Akamai é ativado automaticamente quando o cliente detecta um challenge (status 403 com corpo específico ou presença de /_bm/ na resposta):

// solveAkamai: true por default — funciona automaticamente
const client = new StealthClient({ solveAkamai: true });
const res = await client.get('https://site-com-akamai.com');
// Se receber challenge, o solver: baixa o JS do Akamai, gera sensor_data, refaz o request

// Desativar se o site não usa Akamai (evita overhead)
const client2 = new StealthClient({ solveAkamai: false });

O que o solver faz internamente:

  1. Extrai a URL do script /_bm/... do HTML da página
  2. Baixa o script com cache (não baixa de novo na mesma sessão)
  3. Pega o cookie bm_sz da sessão (seed do algoritmo)
  4. Monta payload com dados do perfil (UA, screen, WebGL, canvas hash, fonts, plugins)
  5. Gera o sensor_data encriptado via akamai-v3-sensor-data-helper
  6. Refaz o request original com o sensor_data e o header akamai-bmsc-token

Exemplo Completo — Fluxo com múltiplos requests

import { StealthClient } from '../src/stealth-client.js';

async function main() {
  const client = new StealthClient({
    browser: 'chrome',
    os: 'windows',
    locale: 'en-US',
    proxyUrl: 'http://user:pass@proxy:8080', // opcional
    timeout: 30_000,
  });

  // 1. GET inicial — coleta cookies de sessão (CSRF, __cf_bm, ASP.NET_SessionId etc.)
  const page = await client.get('https://site.com/pagina-inicial');
  console.log('GET status:', page.status);
  console.log('Cookies coletados:', client.getSessionStats().cookieCount);

  // 2. Extrair token CSRF do HTML se necessário
  const csrfMatch = page.body.match(/name="__RequestVerificationToken" value="([^"]+)"/);
  const csrfToken = csrfMatch?.[1] ?? '';

  // 3. POST com dados — cookies da sessão são enviados automaticamente
  const result = await client.post('https://site.com/api/acao', {
    campo1: 'valor1',
    campo2: 'valor2',
    __RequestVerificationToken: csrfToken,
  }, {
    headers: {
      'Referer': 'https://site.com/pagina-inicial',
    },
  });

  console.log('POST status:', result.status);
  console.log('Resposta:', result.body);
}

main().catch(console.error);

Executar os Exemplos

cd stealth-client

# Testar fingerprint completo (TLS, headers, Cloudflare trace)
npx tsx examples/fingerprint-test.ts

# Exemplo de uso básico
npx tsx examples/usage.ts

# Build TypeScript (gera dist/)
npm run build

# Verificar tipos sem buildar
npm run typecheck

Testar se o Fingerprint Está Correto

Use estes endpoints para verificar:

| URL | O que verifica | |-----|----------------| | https://tls.browserleaks.com/json | JA3, JA4, TLS version, cipher suites | | https://www.cloudflare.com/cdn-cgi/trace | UA, HTTP version, TLS, localização, warp | | https://httpbin.org/headers | Headers enviados exatamente como o servidor recebe | | https://api.ip.cc | IP do proxy (ou IP real se sem proxy) |

Resultado esperado (Chrome real no Windows):

JA4:  t13d1516h2_8daaf6152771_02713d6af862
HTTP: http/2
TLS:  TLSv1.3
kex:  X25519MLKEM768
uag:  Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/145.0.0.0 ...

Erros Comuns e Soluções

StealthError: TIMEOUT

  • Aumentar timeout nas opções
  • Verificar se o proxy está funcionando
  • Testar sem proxy para isolar

StealthError: REQUEST_FAILED — TunnelUnsuccessful

  • Bug do impit com proxies HTTP autenticados
  • Solução: testar com outro proxy ou sem proxy

StealthError: AKAMAI_SOLVE_FAILED — bm_sz não encontrado

  • O GET inicial não retornou o cookie bm_sz
  • O site pode não usar Akamai v3 ou a URL está errada
  • Solução: solveAkamai: false se o site não usa Akamai

StealthError: REQUEST_FAILED — Http2 INTERNAL_ERROR

  • O servidor detectou o fingerprint HTTP/2
  • Pode ser que o site use Akamai v2 que verifica o frame order com mais rigor
  • Solução: testar com browser: 'firefox'

Headers sec-ch-ua inconsistentes com User-Agent

  • NUNCA sobrescreva sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform manualmente
  • Esses valores são gerados pela rede Bayesiana para bater com o UA gerado
  • Se precisar de uma versão específica do Chrome, mude nas opções browser e os

Cloudflare bloqueando POSTs mesmo com GET passando

  • Cloudflare valida __cf_bm e cf_clearance separadamente para POSTs
  • __cf_bm é coletado automaticamente no GET e reenviado nos POSTs via cookie
  • cf_clearance requer execução de JS real (precisa de Playwright/FlareSolverr)
  • Solução sem browser real: só é possível se o __cf_bm for suficiente para o site em questão

Limitações Conhecidas

| Limitação | Motivo | Solução | |-----------|--------|---------| | JS fingerprint (canvas, WebGL real) | Requer GPU física e contexto de browser | Playwright com fingerprint-injector | | cf_clearance (Cloudflare JS Challenge completo) | Requer execução de JS no browser | FlareSolverr ou Playwright headless | | reCAPTCHA v2/v3 | Challenge humano | API de solvers: nextcaptcha, 2captcha, capmonster | | Sites com behavioral analytics avançado | Monitoram mouse, scroll, timing de teclas | Só bypass com browser real + fingerprint injetado |


Adicionando um Novo Script de Automação

Crie em examples/meu-script.ts:

import { StealthClient } from '../src/stealth-client.js';

const client = new StealthClient({
  browser: 'chrome',
  os: 'windows',
  locale: 'en-US',
});

// seu código aqui

// Executar:
// npx tsx examples/meu-script.ts

Regras para não ser detectado:

  1. Sempre faça o GET da página antes dos POSTs — coleta cookies de sessão
  2. Não adicione headers que o Chrome real não envia (X-Forwarded-For, Via, etc.)
  3. Não sobrescreva sec-ch-ua, User-Agent, Sec-Fetch-* — o cliente gera tudo correto
  4. Se o site pede Referer nos POSTs, passe via headers: { Referer: 'URL' } nas opções
  5. Use locale: 'en-US' para sites americanos (o Accept-Language precisa bater com o alvo)
  6. Evite múltiplos requests rápidos — o humanDelay (800-2500ms) só é aplicado no Akamai solver; implemente seus próprios delays se necessário