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

@jevaas/sdk

v0.3.3

Published

SDK TypeScript oficial do JEVaaS (jevaas.com.br) — julgamento tipado, roteado e auditável

Readme

@jevaas/sdk

SDK TypeScript oficial do JEVaaS (jev.vertikon.com.br) — o julgamento tipado e roteado descrito no contrato de borda.

Zero dependências de runtime: só fetch nativo (Node 18+).

npm i @jevaas/sdk

Começando

import { Jevaas, JevaasError, isActionable } from '@jevaas/sdk';

const jevaas = new Jevaas({ apiKey: process.env.JEVAAAS_API_KEY! });

const decisao = await jevaas.judge({
  contract: 'ticket-router',                 // usa a versão vigente
  state: { ticket: { messages: [{ author: 'customer', text: '…' }] } },
  state_version: 'run_184:step_7',           // opcional, para rastreio
  decision_id: 'tkt_9931',                   // opcional, idempotente no inquilino
});

if (isActionable(decisao)) {
  // só aqui: route === 'auto' E enforced === true
  await minhaPolitica.executar(decisao.allow!);
} else {
  console.log(describeRoute(decisao.route, decisao.status));
}

| opção | default | papel | | --- | --- | --- | | apiKey | — | obrigatória; precisa começar com jev_sk_ | | baseUrl | https://jev.vertikon.com.br/v1 | borda da vertical; validada no construtor | | allowPrivateEndpoints | false | aceita destino privado/loopback em baseUrl (dev local); nunca em produção | | timeoutMs | 30000 | timeout por request | | maxRetries | 3 (mínimo 1) | tentativas totais em 429/502/503/504 e falha de rede | | onRequest | — | observabilidade: recebe cada tentativa concluída |

O serviço nunca executa nada. A resposta traz julgamento, rota e um allow opaco; quem mapeia allow para permissão concreta é o motor de política do consumidor. Em mode: shadow o julgamento vem com enforced: false — ele mede, não autoriza.

Métodos

| método | rota | escopo | | --- | --- | --- | | judge(req) | POST /decisions/judge | read | | fanout(req) | POST /decisions/fanout | read | | contracts.list() | GET /contracts | read | | contracts.get(id) | GET /contracts/{id} | read | | contracts.create(input) | POST /contracts | write | | contracts.update(id, input) | PUT /contracts/{id} | write | | contracts.versions(id) | GET /contracts/{id}/versions | read | | contracts.version(id, v) | GET /contracts/{id}/versions/{v} | read | | contracts.promote(id, v) | POST /contracts/{id}/versions/{v}/promote | contracts | | contracts.setMode(id, mode) | POST /contracts/{id}/mode | contracts | | receipts.list(filter) | GET /receipts | read | | receipts.get(id) | GET /receipts/{id} | read | | receipts.label(id, l) | POST /receipts/{id}/label | write | | receipts.outcome(id, o) | POST /receipts/{id}/outcome | write | | calibration(contract, opts?) | GET /calibration/{contract} | read | | runEval(contract, cases) | POST /evals/{contract}/run (corpo: casos) | contracts | | me() · quota() · usage() | GET /auth/me · /quota · /usage | read |

fanout não tem contrato, regra nem barra: devolve só as respostas tipadas, e status: 'decided' não autoriza nada.

Exemplos por recurso

// contratos: publicar mudança é criar versão nova — a anterior fica imutável, para poder reverter
const v1 = await jevaas.contracts.create({
  id: 'ticket-router',
  description: 'Roteia tickets de suporte',
  owner: '[email protected]',
  action_class: 'internal_write',            // a barra pertence à classe, não ao modelo
  primary_question: 'fila_principal',
  questions: {
    fila_principal: {
      type: 'choice',
      instructions: 'Qual equipe deve tratar a solicitação principal?',
      criteria: { vendas: '…', suporte: '…', indeterminado: 'Evidência insuficiente.' }, // escape obrigatório
    },
  },
  routes: [{ when: { question: 'fila_principal', equals: 'suporte' }, route: 'auto', allow: 'support:route' }],
  default_route: 'human_review',             // nunca 'auto' (§5)
});
await jevaas.contracts.promote('ticket-router', 2);
await jevaas.contracts.setMode('ticket-router', 'enforce');

// recibos: no modo sombra, devolver o desfecho real é o que permite medir divergência
const { receipts } = await jevaas.receipts.list({ contract: 'ticket-router', route: 'human_review', limit: 50 });
await jevaas.receipts.outcome(receipts[0].receipt_id, { production_answer: 'suporte', action_taken: true });
await jevaas.receipts.label(receipts[0].receipt_id, { human_label: 'suporte' });

// calibração: "as respostas de confiança alta são mesmo mais confiáveis nesta carga?"
const rel = await jevaas.calibration('ticket-router');
const golden = await jevaas.runEval('ticket-router', [
  { name: 'fatura', expected: 'financeiro', state: { ticket: { messages: [{ author: 'customer', text: 'fatura em dobro' }] } } },
]); // {total, correct, accuracy, by_band, failures}

Erros e resiliência

Toda resposta ≥ 400 vira JevaasError com status, requestId, body e o getter retryable:

try {
  await jevaas.judge({ contract: 'ticket-router', state });
} catch (e) {
  if (e instanceof JevaasError) {
    console.error(`API ${e.status}: ${e.message} (request ${e.requestId})`);
    if (e.retryable) await agenda.retentar();
  }
  throw e;
}
  • retryable é true para 0 (rede/timeout), 429, 502, 503 e 504.
  • O retry é automático até maxRetries, honrando Retry-After (em segundos) quando presente e caindo em backoff exponencial com jitter (250ms × 2ⁿ⁻¹, teto de 8s) quando não.
  • A mensagem prefere body.error, depois body.code, depois o status HTTP.
  • 422 invalid_contract não é retentado: é contrato reprovado, não indisponibilidade. 409 também não: publique outra versão.

Helpers

  • isActionable(res) — true somente com route === 'auto' e enforced === true. É o portão que separa julgamento de autorização: em sombra, collect_evidence, human_review ou abstain, nunca aja.
  • describeRoute(route, status?) — frase curta do que fazer com a decisão ("Busque evidência NOVA e rejulgue; não age."), considerando o status primeiro (pending_reconciliation pede conferir consumo, stale pede rejulgar).
  • isPublicEndpoint(baseUrl) — true quando a URL passa na guarda de destino do construtor (http/https e host público). Mesma régua, sem lançar: confira a configuração antes de construir.

Destino (SSRF)

O construtor recusa baseUrl que não seja http/https e host público: loopback, faixas privadas, link-local, reservadas e multicast (IPv4 e IPv6, inclusive ::ffff:127.0.0.1 e CGNAT 100.64/10) e os nomes localhost, *.localhost, *.local e *.internal. Nome DNS que não seja IP literal passa — resolver está fora do escopo do SDK, quem resolve é o conector. Para apontar para um servidor local (dev, teste), allowPrivateEndpoints: true — nunca em produção.

Tipos

QuestionKind, Question, QuestionCriteria, Answer, Route, Status, ActionClass, Mode, Threshold, StateField, StateRole, Condition, RouteRule, DecisionContract, ContractInput, Receipt, ReceiptListFilter, ReceiptListResponse, JudgeRequest, JudgeResponse, JudgeContractRef, FanoutRequest, FanoutResponse, CalibrationReport, EvalReport, Usage, AccountInfo, Quota, UsageReport.

Os formatos seguem o contrato de borda §4. Onde o contrato fixa o significado mas não os nomes dos campos (/calibration, /quota, /usage, /auth/me), o tipo tem índice aberto: leia o que existe em vez de esperar o que não foi prometido.

Mudanças na 0.3.3

  • Escolha do modelo. O JEVaaS oferece o Jev (TypeSafe) e o Drex (nace.ai), com o mesmo preço por token. models() lista o que está disponível; escolha com model no fanout ou no contrato (ContractInput.model). Contrato com modelo sem tarifa é recusado na publicação (422). No Drex, instrução e critérios precisam ser texto. Perfis medidos em português: jevaas.com.br/model#escolha.

Mudanças na 0.3.2

  • when.all nas regras do contrato: uma regra pode olhar várias perguntas ao mesmo tempo, com E entre elas (um nível só). As perguntas de um pedido não se veem, então a contradição entre elas vira rota — ex.: fila suporte e urgência imediata → human_review.
  • audit_sample_rate no contrato: fração das decisões auto sorteadas para auditoria. A rota não muda; a resposta e o recibo trazem audit: true, e receipts.list({ audit: true }) devolve a fila. Rotule com receipts.label para medir a taxa de falso-auto.
  • Boas práticas de decisão: texto julgado não confiável (injeção medida), falha fechada e limiar como custo.

Mudanças na 0.3.1

  • redact_identity no fanout e no contrato: o JEVaaS tira do estado as menções em que a pessoa declara raça, religião, orientação sexual, identidade de gênero, gênero, deficiência ou idade (PT e EN) antes de julgar. A resposta traz redaction: { mentions, kinds } (tipo RedactionReport). Medido em produção: o efeito de identidade na moderação caiu de até +12,6 pontos para perto de zero — viés e decisões reguladas.

Mudanças na 0.3.0

  • Quebra (corrige): runEval(contract, cases) envia os casos do golden set no corpo. Na 0.2.0 o método chamava sem corpo e a API sempre respondia 400. Para fixar a versão avaliada, informe contract_version nos casos (o opts.version saiu: a API o ignorava).
  • Host padrão: baseUrl passa a ser https://jev.api.br/v1. O host anterior (api.jev.vertikon.com.br) continua respondendo; quem o fixou não precisa mudar nada.
  • Escopo contracts: runEval, contracts.setMode e contracts.promote pedem contracts (ou admin). No painel, marque "pode promover contratos" ao criar a chave.
  • Tipo GoldenCase exportado.

Mudanças na 0.2.0

Atualize manualmente se você declarou ^0.1.0. Em versões 0.x, o ^ não atravessa minor (^0.1.0 = >=0.1.0 <0.2.0): um npm update não traz a 0.2.0, e a 0.1.0 é a versão em que o retry podia cobrar o mesmo julgamento duas vezes. Use npm install @jevaas/sdk@^0.2.0 (ou fixe a versão exata).

  • judge() gera decision_id quando você não passa — uma vez por chamada, então o retry automático reenvia a mesma chave e o serviço devolve o recibo em vez de julgar e cobrar de novo. Continue passando o id de negócio (tkt_…) quando tiver: ele é preservado e é o que protege entre processos.
  • fanout() não repete mais em falha ambígua (rede/timeout, 504): o serviço não deduplica fanout, e a cobrança pode ter ocorrido. Trate status 0/504 como "pode ter sido cobrado". 429/502/503 continuam sendo repetidos.

Licença

MIT © Vertikon