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

@aniyan-project/branilist-api

v0.1.0

Published

Typed, configurable GraphQL SDK for the Branilist public API.

Readme

@aniyan-project/branilist-api

SDK em TypeScript e JavaScript para a API pública GraphQL do Branilist. Selecione os campos de que precisa e receba respostas com tipos inferidos, sem escrever manualmente os tipos de cada operação.

  • Cobertura do schema público: catálogo, perfis, biblioteca, coleções, favoritos, comunidade, notificações e fórum.
  • OAuth com PKCE S256, renovação e revogação de tokens; API keys para integrações compatíveis.
  • Configuração de endpoint, credenciais dinâmicas, headers, fetch, timeout, cancelamento e tentativas de leitura.
  • Paginação assíncrona, aliases, consultas GraphQL livres e acesso a respostas parciais.
  • Distribuições ESM e CommonJS, declarações TypeScript e nenhuma dependência de execução.

Documentação da API · Termos da API · Guia de contribuição · Publicação

Instalação e compatibilidade

Instale o pacote público da organização aniyan-project:

npm install @aniyan-project/branilist-api

Node.js 22 ou superior. Também pode ser usado em aplicações web com bundler e fetch, AbortController e URL disponíveis. Em React Native, forneça implementações compatíveis quando necessário; PKCE requer criptografia segura. O pacote não inclui polyfills nem armazenamento de credenciais. A integração com cada ambiente mobile deve ser validada pelo aplicativo.

import { createClient, MediaType } from '@aniyan-project/branilist-api'

const client = createClient({ apiKey: 'SUA_API_KEY' })
const result = await client.query({
  mediaPage: {
    __args: { search: 'Frieren', type: MediaType.ANIME, first: 10 },
    nodes: { id: true, slug: true, title: { romaji: true }, coverImage: true },
    pageInfo: { limit: true, offset: true, hasNextPage: true },
  },
})

console.log(result.mediaPage.nodes[0]?.title.romaji)

CommonJS: const { createClient } = require('@aniyan-project/branilist-api').

API keys devem permanecer no servidor. Aplicativos públicos web/mobile devem usar OAuth para acessar dados de usuários. O SDK não altera as exigências de autenticação e escopos de cada campo.

Seleção de campos e identificadores

Use true para campos escalares, objetos para seleções aninhadas e __args para argumentos. Valores são enviados como variáveis GraphQL, sem interpolação no documento. Campos não selecionados ficam ausentes do tipo da resposta. Campos anuláveis continuam exigindo tratamento de null.

IDs, incluindo MyAnimeList e AniList, são strings, preservando a precisão de identificadores grandes. Datas Time são strings ISO, sem conversão automática para Date.

const { media } = await client.query({
  media: {
    __args: { malId: '52991' }, // Use exatamente um: id, malId ou anilistId.
    id: true,
    title: { romaji: true, portuguese: true },
    episodes: true,
    anilistId: true,
  },
})
console.log(media?.title.romaji)

const users = await client.query({
  __alias: {
    byId: { user: { __args: { id: '123' }, username: true } },
    byName: { user: { __args: { username: '123456' }, id: true } },
  },
})
console.log(users.byName?.id)

Cada alias seleciona exatamente um campo. Use os argumentos explícitos de user para diferenciar IDs e usernames numéricos. A visibilidade dos perfis e das listas é verificada pela API.

Biblioteca e ações autenticadas

const authenticated = createClient({
  // Resolvido a cada tentativa. Leia do armazenamento seguro do aplicativo.
  accessToken: async () => getCurrentAccessToken(),
})

const saved = await authenticated.mutation({
  saveLibraryEntry: {
    __args: { mediaId: '123', input: { status: 'CURRENT', progress: 5 } },
    mediaId: true,
    status: true,
    progress: true,
  },
})

const removed = await authenticated.mutation({
  removeLibraryEntry: {
    __args: { mediaId: '123' },
    mediaId: true,
    removed: true,
  },
})

getCurrentAccessToken representa uma função do seu aplicativo. Essas operações exigem library:write e atuam na biblioteca do dono do token. Mutations não são repetidas automaticamente.

O mesmo cliente oferece os demais recursos do schema:

| Área | Exemplos | | --- | --- | | Catálogo | media, mediaPage, mediaBySlug, trendingMedia, genres, staff, character, studio | | Conta e perfis | user, viewer.profile, preferências, privacidade e atualização de perfil | | Biblioteca | Listagem, pesquisa, entrada individual, insights, atualização e remoção | | Coleções e favoritos | Coleções públicas/pessoais, criação, edição, itens e ordenação de favoritos | | Comunidade | Feed, posts, reviews, comentários, curtidas, reações e compartilhamentos | | Relações e notificações | Seguir, bloquear, silenciar, notificações e preferências | | Fórum | Categorias, tags, tópicos, respostas, espaços, inscrições, reações e participação | | Denúncias | Envio de denúncias permitidas ao usuário autenticado |

A versão inicial acompanha 24 queries e 41 mutations, além dos campos aninhados. Não inclui operações administrativas. Consulte os escopos e regras da API antes de solicitar consentimento.

Configuração

const configured = createClient({
  endpoint: 'https://branilist.com/api/graphql',
  accessToken: () => getCurrentAccessToken(),
  timeoutMs: 15_000,
  retry: { maxRetries: 2, baseDelayMs: 250, maxDelayMs: 5_000 },
  headers: { 'X-Client-Version': '1.0.0' },
  // fetch: suaImplementacaoFetch,
  onResponse: ({ status, rateLimit, durationMs }) => {
    console.log({ status, rateLimit, durationMs })
  },
  onRetry: ({ attempt, delayMs }) => console.log({ attempt, delayMs }),
})

const controller = new AbortController()
await configured.query({ api: { name: true, version: true } }, {
  signal: controller.signal,
  timeoutMs: 5_000,
  operationName: 'APIInfo',
  headers: { 'X-Request-ID': 'request-123' },
})
// controller.abort() cancela uma requisição pendente.

accessToken, apiKey e headers aceitam valor ou função síncrona/assíncrona. OAuth e API key são mutuamente exclusivos. Prefira essas opções a definir manualmente headers de autenticação. Não compartilhe um cliente com token mutável global entre usuários de um servidor SSR.

O endpoint padrão é o público /api/graphql. HTTPS é obrigatório; HTTP é aceito em loopback ou mediante allowInsecureHTTP: true para desenvolvimento. Cookies são omitidos e redirecionamentos são rejeitados pelo fetch padrão. Uma implementação de fetch própria precisa respeitar essas opções.

O timeout cobre obtenção de credenciais, rede, leitura do corpo e esperas entre tentativas. Leituras construídas por query podem repetir falhas de rede e HTTP 408/429/500/502/503/504, até duas vezes por padrão, respeitando Retry-After dentro do prazo total. Erros GraphQL não disparam repetição. retry: false desativa as tentativas adicionais.

Mutations e documentos livres não repetem por padrão. A opção idempotent: true permite repetição explícita: use apenas se toda a operação puder ser executada novamente com segurança. Os hooks são síncronos; exceções neles interrompem a operação. Não registre tokens, variáveis privadas nem respostas pessoais.

Um timeout ou cancelamento não confirma que o servidor desfez uma alteração já recebida. Confira o estado antes de repetir uma ação cujo resultado ficou desconhecido.

Os metadados expõem status HTTP, tentativa, duração e os headers de limites conhecidos. Headers ausentes aparecem como null, sem estimar cota disponível.

Paginação

import { paginate } from '@aniyan-project/branilist-api'

for await (const item of paginate(async (page, context) => {
  const result = await authenticated.query({
    viewer: {
      libraryPage: {
        __args: page,
        nodes: { mediaId: true, progress: true },
        pageInfo: { limit: true, offset: true, hasNextPage: true },
      },
    },
  }, context)
  if (!result.viewer?.libraryPage) throw new Error('Biblioteca indisponível')
  return result.viewer.libraryPage
}, { first: 20, maxPages: 5 })) {
  console.log(item.mediaId)
}

paginate entrega itens; iteratePages entrega páginas. Ambos são preguiçosos, sem pré-carregamento, e aceitam first, offset, maxPages e signal. Encaminhe context ao cliente para cancelar também uma requisição em andamento. Limites: first entre 1 e 100, offset entre 0 e 10000. A paginação não concede autorização para coleta em massa; delimite a consulta ao uso do aplicativo.

Documentos GraphQL e respostas parciais

import { GraphQLRequestError, HTTPError } from '@aniyan-project/branilist-api'

type APIData = { api: { name: string; version: string } }
const info = await client.request<APIData>(`
  query Info { api { name version } }
`)

try {
  await authenticated.query({ viewer: { id: true } })
} catch (error) {
  if (error instanceof GraphQLRequestError) console.log(error.codes)
  if (error instanceof HTTPError) console.log(error.status, error.retryAfterMs)
}

const response = await client.rawRequest<APIData>(
  'query Info { api { name version } }',
  {},
  { errorPolicy: 'all' },
)
console.log(response.data, response.errors, response.metadata)

request e query lançam GraphQLRequestError quando há erros, inclusive com HTTP 200 e dados parciais. rawRequest com errorPolicy: 'all' preserva data e errors; falhas HTTP, JSON inválido, timeout e cancelamento continuam lançando exceções. Classes exportadas: BranilistError, ConfigurationError, HTTPError, GraphQLRequestError, ProtocolError, NetworkError, TimeoutError, RequestAbortedError e OAuthError.

Documentos livres permitem fragments e directives. Nesse modo, o tipo genérico da resposta é responsabilidade da aplicação. O SDK não executa introspecção em tempo de execução. Para montar e inspecionar operações tipadas sem enviá-las, use buildQuery e buildMutation; o resultado pode ser passado a request/rawRequest.

OAuth com PKCE

Cadastre um aplicativo no Branilist e utilize exatamente a URI de redirecionamento registrada.

import { BranilistOAuth, createPKCE, validateAuthorizationCallback } from '@aniyan-project/branilist-api'

const oauth = new BranilistOAuth({
  clientId: 'SEU_CLIENT_ID',
  redirectUri: 'meuapp://oauth/callback',
})
const pkce = await createPKCE()
const authorizeURL = oauth.authorizationURL(pkce, ['profile:read', 'library:read', 'library:write'])
// Guarde verifier e state por tentativa em armazenamento seguro e abra authorizeURL.
// Ao receber o callback, recupere os valores guardados para essa tentativa:
const code = validateAuthorizationCallback(callbackURL, savedState)
const tokens = await oauth.exchangeCode(code, savedVerifier)
// Persista access_token, refresh_token e sua expiração com segurança.

const rotated = await oauth.refreshToken(savedRefreshToken)
// Substitua imediatamente o refresh token antigo pelo novo.
await oauth.revokeToken(rotated.refresh_token)

callbackURL, savedState, savedVerifier e savedRefreshToken vêm do seu aplicativo. O SDK não abre telas nem persiste tokens. Não reutilize verifier/state. Renovação rotaciona o refresh token: serialize renovações concorrentes e persista a nova resposta. Troca, renovação e revogação nunca repetem automaticamente. Não embuta clientSecret em apps mobile ou navegadores; essa opção existe somente para clientes confidenciais em servidores.

createPKCE(cryptoProvider) aceita uma implementação segura compatível com Web Crypto para ambientes sem suporte nativo. Nunca substitua aleatoriedade criptográfica por Math.random.

Desenvolvimento

npm ci
npm run check
npm run test:package
npm pack

Os testes verificam operações do schema, inferência TypeScript, erros, retries, cancelamento, paginação, PKCE, OAuth, fetch real local e entradas ESM/CommonJS. Não executam mutations contra produção.

O schema distribuído está em schema.graphql. Para acompanhar alterações na documentação pública:

npm run schema:sync
npm run generate
npm run check

Também é possível importar um SDL local com npm run schema:sync -- --file caminho/schema.graphql. A geração é determinística; o CI rejeita arquivos gerados desatualizados. Mudanças no schema precisam de revisão e nova versão do pacote; instalações existentes não mudam silenciosamente.

Licença e uso da API

O código deste SDK está disponível sob MIT. O acesso ao serviço Branilist segue os Termos de Uso da API, incluindo regras de uso comercial, privacidade, nomenclatura e coleta de dados. A licença do SDK não substitui esses termos, não fornece credenciais e não remove escopos ou limites do serviço.