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

@vitongovisk/forge

v1.5.0

Published

> **Opinionated frontend architecture generator for TypeScript projects.** > Versão `2.0` · Publicado como `@vitongovisk/forge`

Readme

⚒️ Forge CLI

Opinionated frontend architecture generator for TypeScript projects.
Versão 2.0 · Publicado como @vitongovisk/forge

Forge é um CLI que gera automaticamente todas as camadas de integração com APIs REST no seu frontend: tipos de contrato, cliente HTTP, serviços e hooks TanStack Query — tudo fortemente tipado, a partir de um único comando.


Pré-requisitos

Seu projeto deve ter as seguintes dependências instaladas:

npm install axios @tanstack/react-query

O Forge verifica essas dependências no forge init e oferece instalação automática caso não estejam presentes.

Node.js: >= 18


Instalação

npm install -D @vitongovisk/forge

Ou use globalmente:

npm install -g @vitongovisk/forge

Início Rápido

# 1. Inicializar o Forge no projeto
forge init

# 2. Criar um módulo
forge module users

# 3. Adicionar endpoints ao módulo
forge add users getUser --get
forge add users createUser --post
forge add users updateUser --put
forge add users deleteUser --delete

Pronto. O Forge gera toda a estrutura de arquivos para você.


Multi-Backend

Se o seu projeto consome mais de uma API (ex: API principal + serviço de pagamentos + serviço de analytics), use o comando forge client para registrar instâncias Axios adicionais:

# 1. Registrar um novo client (instância Axios)
forge client payments
# → injeta `paymentsApi` no api-client.ts e registra em forge.data.json

# 2. Criar um módulo que consome esse client
forge module billing --client paymentsApi

# 3. Adicionar endpoints — herda o client do módulo automaticamente
forge add billing chargeUser --post     # usa paymentsApi
forge add billing refund --post --client api   # override por endpoint

Usuários single-backend (só api) não veem essas opções — o fluxo continua idêntico ao Quick Start. Detalhes de cada comando abaixo.


Comandos

forge init

Inicializa o Forge no projeto atual.

forge init
forge init --overwrite   # Reconfigura um projeto já inicializado

O que faz:

  • Cria o arquivo de configuração .forge.json (inclui a versão do Forge usada — forgeVersion — pra suportar migrações futuras)
  • Cria forge.data.json (registro de intenção dos módulos, com clients: ["api"] por padrão)
  • Cria forge.state.json (estado real dos arquivos gerados)
  • Gera o api-client.ts com Axios configurado (interceptors de autenticação JWT e unwrap de response.data)
  • Gera os tipos base compartilhados (api.types.ts, global.types.ts, shared.types.ts)
  • Gera o utilitário api.utils.ts com parseApiError

Pergunta apenas: modo de sincronização (auto ou manual).


forge module <name> · alias: forge m

Cria um novo módulo com a estrutura base completa.

forge module users
forge m auth

# Módulo sem tipos inicialmente (para módulos utilitários como auth, session)
forge module auth --minimal

# Multi-backend: especificar qual API client o módulo consome
forge module billing --client paymentsApi

Opções:

| Flag | Descrição | |---|---| | --minimal | Pula a criação de <module>.types.ts e contracts/ | | --client <name> | Declara qual instância Axios o módulo consome (default: api). Valida contra os clients registrados em forge.data.json | | --sync / --no-sync | Força ou pula a sincronização automática após criação |

Se houver mais de um client registrado e --client não for passado, o Forge entra em modo interativo perguntando qual usar.

Estrutura gerada (padrão):

src/modules/users/
├── users.api.ts       ← cliente HTTP tipado
├── users.service.ts   ← camada de serviço/negócio
└── users.types.ts     ← interface base do modelo

Com --minimal (sem users.types.ts e sem contracts/):

src/modules/auth/
├── auth.api.ts
└── auth.service.ts

forge client <name> · alias: forge c

Registra uma nova instância Axios no api-client.ts, permitindo que módulos consumam APIs diferentes (ex: serviço de pagamentos, analytics, autenticação separada).

forge client payments
forge c analytics

O que faz:

  1. Injeta no api-client.ts (via AST, preserva edições manuais existentes):
    export const paymentsApi = axios.create({
      baseURL: import.meta.env.VITE_API_PAYMENTS_URL,
      timeout: 1000 * 15,
    });
    setupInterceptors(paymentsApi);
  2. Registra "paymentsApi" em forge.data.jsonclients.
  3. Roda sync automaticamente (se sync.mode === "auto").

Convenção de nomes: você passa o nome base (ex: payments), o Forge cria a instância com sufixo Api (ex: paymentsApi). A env var segue o padrão VITE_API_<UPPER_SNAKE>_URL (ex: VITE_API_PAYMENTS_URL).

Validação: ao usar forge module --client X ou forge add --client X, o nome X precisa estar registrado em forge.data.json. Caso contrário, o Forge erra com a lista dos clients disponíveis.


forge add <module> <functionName> · alias: forge a

Adiciona um endpoint a um módulo existente, gerando todas as camadas verticalmente.

# Com flag de método explícita
forge add users getUser --get
forge add users createUser --post
forge add users updateUser --put
forge add users deleteUser --delete

# Aliases
forge a users getUser --get

# Sem flag → modo interativo
forge add users getUser
# → Escolha o método:
#   1. GET
#   2. POST
#   3. PUT
#   4. DELETE

# Override por endpoint (multi-backend)
forge add billing refund --post --client api
# → o módulo "billing" usa paymentsApi por default, mas esse
#   endpoint específico vai falar com o client "api".
#   O Forge adiciona o import de "api" no billing.api.ts
#   automaticamente, sem quebrar os outros endpoints.

Resolução do client (em ordem de prioridade):

  1. Flag --client X (se passada e válida).
  2. Client salvo no módulo (forge.data.json).
  3. Único client registrado (sem prompt).
  4. Prompt interativo (se houver >1 client e o módulo ainda não tem um salvo — auto-heal: salva no módulo após a escolha).

Convenção de URLs: módulos e ações são convertidos para kebab-case ao gerar o path HTTP:

| Comando | URL gerado | |---|---| | forge add userProfile createUser --post | POST /user-profile/create-user | | forge add billing chargeUser --post | POST /billing/charge-user | | forge add userProfile getUser --get | GET `/user-profile/${payload.params.id}` | | forge add billing refund --delete | DELETE `/billing/${payload.params.id}` |

GET, PUT e DELETE usam o id do payload (não o nome da função) — convenção REST padrão. POST usa /<módulo>/<ação> no kebab.

O que é gerado por chamada:

| Arquivo | Descrição | |---|---| | contracts/getUser.types.ts | Tipos de Payload, Response e mapa de erros da operação | | Injeção em users.api.ts | Método getUser adicionado via AST ao objeto UsersAPI | | Injeção em users.service.ts | Método getUser com try/catch adicionado ao UsersService | | hooks/useGetUser.hook.ts | Hook useQuery ou useMutation do TanStack Query |

Estrutura final do módulo após 4 endpoints:

src/modules/users/
├── contracts/
│   ├── createUser.types.ts
│   ├── deleteUser.types.ts
│   ├── getUser.types.ts
│   ├── updateUser.types.ts
│   └── index.ts          ← barrel: export * de cada *.types
├── hooks/
│   ├── useCreateUser.hook.ts
│   ├── useDeleteUser.hook.ts
│   ├── useGetUser.hook.ts
│   ├── useUpdateUser.hook.ts
│   └── index.ts          ← barrel: export * de cada hook
├── users.api.ts
├── users.service.ts
├── users.types.ts
└── index.ts              ← barrel: api + service + types + hooks/ + contracts/

Os arquivos index.ts (barrel exports) são regenerados do zero a cada add/remove/rename — listam alfabeticamente o que existe no disco. Resultado:

// Em vez de:
import { useGetUser } from '@/modules/users/hooks/useGetUser.hook';
import { UsersService } from '@/modules/users/users.service';
import { type GetUserPayload } from '@/modules/users/contracts/getUser.types';

// Você pode:
import { useGetUser, UsersService, type GetUserPayload } from '@/modules/users';

Importante: os index.ts são overwritten a cada operação. Se você editar à mão pra customizar (ex: renomear um re-export), perde na próxima geração. Pra módulos --minimal, o index.ts do módulo omite a linha de types; hooks/index.ts e contracts/index.ts só existem quando há arquivos a re-exportar (são deletados se todos os endpoints forem removidos).


forge sync · alias: forge s

Sincroniza o estado interno do Forge com o estado real do filesystem.

forge sync

O que faz:

  • Verifica quais arquivos existem fisicamente no disco
  • Atualiza o forge.state.json com o estado real
  • Detecta módulos incompletos (api ou service faltando)
  • Detecta módulos órfãos (existem no disco mas não foram registrados)
  • Parseia api-client.ts e cruza com data.clients:
    • missing — em data.clients mas sumiu do arquivo
    • órfão — no arquivo mas não em data.clients
  • Detecta módulos apontando pra clients que sumiram (link broken)
  • Exibe um relatório detalhado no terminal

Relatório de exemplo (estado degradado):

🔄 Sincronizando projeto...

✅ Sync finalizado!

📊 Módulos: 2 ativos, 1 incompletos, 1 órfãos
🔌 Clients: 1 ativos, 1 missing, 1 órfãos

⚠️  Módulos com arquivos faltando:
   - billing (faltando: service, contracts)

👻 Módulos órfãos detectados:
   - legacy-module

   💡 Dica: rode 'forge module legacy-module' para registrá-lo.

⚠️  Clients missing (registrados em data, sumiram do api-client.ts):
   - paymentsApi
   💡 Dica: rode 'forge client payments' pra recriar.

👻 Clients órfãos (no api-client.ts, mas não registrados):
   - analyticsApi
   💡 Dica: rode 'forge client analytics' pra registrá-los.

💥 Módulos apontando pra clients inexistentes:
   - billing → paymentsApi
   💡 Dica: recrie o client ou edite o módulo pra usar outro client registrado.

Os comandos forge module --client X e forge add --client X também fazem pré-check live contra o api-client.ts — se o client estiver registrado em data.clients mas tiver sumido do arquivo, o comando aborta com a mesma dica de forge client X pra recriar.


forge list · alias: forge l

Lista todos os módulos registrados com seu status.

forge list
forge l

Exemplo de saída:

📦 Módulos (3 total)

  ✅ users — 4 método(s)
  ✅ billing — 2 método(s)
  ⚠️  legacy  ← incompleto (faltando: service, contracts)

Status possíveis: active (tudo no lugar), missing (arquivo crítico faltando), orphan (existe no disco mas não está registrado em forge.data.json).


forge describe <module> · alias: forge d

Exibe um relatório detalhado sobre um módulo registrado: status, client, endpoints (com verbo HTTP detectado do api.ts), e a presença de cada arquivo.

forge describe billing
forge d billing

Exemplo de saída:

📦 Module: billing  ✅ active
   Client: paymentsApi

🔌 Endpoints (2):
   POST   chargeUser
   POST   refund   (using api — override)

📁 Files:
   ✅ src/modules/billing/billing.api.ts
   ✅ src/modules/billing/billing.service.ts
   ✅ src/modules/billing/billing.types.ts
   ✅ src/modules/billing/contracts  (2 files)
   ✅ src/modules/billing/hooks      (2 files)

Quando um endpoint usa um client diferente do default do módulo (override por endpoint), aparece o marcador (using X — override) na frente do método. Para módulos missing/orphan, o comando exibe dicas sobre como reconciliar.


forge remove <module> <functionName> · alias: forge rm

Remove um endpoint do módulo, inverso do forge add. Limpa:

  • Arquivo contracts/<fn>.types.ts
  • Arquivo hooks/use<Fn>.hook.ts
  • Propriedade <fn> do objeto <Module>API em *.api.ts (via AST)
  • Propriedade <fn> do objeto <Module>Service em *.service.ts (via AST)
  • Imports dos tipos do contrato em *.api.ts e *.service.ts
  • Imports de clients axios que ficaram órfãos (ex: se o endpoint era um override por --client, o import { api } é removido quando nenhum outro método usar api)
  • Entrada em forge.data.jsonresources[].methods
forge remove user deleteSession
forge rm billing refund

# Pula a confirmação interativa (útil em scripts)
forge rm user teste1 -y
forge rm user teste1 --yes

Confirmação: por default, lista os arquivos/propriedades que vão sumir e pede y/N antes de executar. Passa -y/--yes pra confirmar automaticamente.

Erros e edge cases:

  • Módulo inexistente → erro com sugestão forge list.
  • Endpoint inexistente → erro com sugestão forge describe <module>.
  • Endpoint parcialmente presente (ex: arquivo no disco mas não em data.json) → remove o que encontrar e atualiza data.json.

forge rename <module> <oldName> <newName> · alias: forge rn

Renomeia um endpoint em todas as camadas — propriedades em *.api.ts/*.service.ts, tipos do contrato (Payload, Response, ValidationError, ErrorMap), função do hook, paths de import, nome dos arquivos, e a entrada em forge.data.json. POST também tem a URL kebab atualizada (/<módulo>/<old-kebab>/<módulo>/<new-kebab>); GET/PUT/DELETE usam ${payload.params.id} na URL e ficam intactos.

forge rename billing chargeUser chargeCustomer
forge rn user deleteSession revokeSession

# Pula a confirmação interativa
forge rn billing chargeUser chargeCustomer -y

Resolução: se o nome novo normalizar pro mesmo camelCase do antigo (ex: charge-user vs chargeUser), erro — nada a renomear.

Erros e edge cases:

  • Módulo inexistente → erro com sugestão forge list.
  • oldName não existe em <module>.api.ts → erro com sugestão forge describe <module>.
  • newName já existe → erro pedindo outro nome ou forge rm <module> <newName> antes.
  • Imports do contrato/hook em código de aplicação não são atualizados — só os arquivos que o Forge gerou. Se você importou tipos do contrato em outros lugares, atualize esses paths à mão (TypeScript vai apontar).

Arquivos de Configuração

.forge.json

Configuração principal do Forge (gerada pelo init, raramente editada manualmente).

{
  "forgeVersion": "1.2.0",
  "project": {
    "architecture": "module",
    "modulePath": "src/modules",
    "apiFile": "src/modules/api/api-client.ts"
  },
  "sync": {
    "mode": "auto"
  }
}

| Campo | Valores | Descrição | |---|---|---| | forgeVersion | string | Versão do Forge que criou/atualizou o projeto. Usado pra migrações futuras | | architecture | "module" | Sempre modular no V2 | | modulePath | string | Pasta raiz dos módulos | | apiFile | string | Caminho do api-client.ts | | sync.mode | "auto" | "manual" | Se auto, sync roda após cada operação |

forge.data.json

Registro de intenção — o que você declarou ao Forge que existe no projeto.

{
  "clients": ["api", "paymentsApi"],
  "resources": [
    {
      "name": "users",
      "methods": ["getUser", "createUser", "updateUser", "deleteUser"],
      "client": "api"
    },
    {
      "name": "billing",
      "methods": ["chargeUser", "refund"],
      "client": "paymentsApi"
    }
  ]
}

| Campo | Descrição | |---|---| | clients | Instâncias Axios registradas (default ["api"], expandido via forge client) | | resources[].client | Qual client esse módulo usa por padrão (passa pro scaffold + cada add) |

forge.state.json

Registro de realidade — o que o Forge verificou que existe no filesystem.

{
  "project": { "architecture": "module", "lastSync": "2026-05-22T00:00:00.000Z" },
  "sync": { "mode": "auto", "lastRun": "2026-05-22T00:00:00.000Z" },
  "resources": [
    {
      "name": "users",
      "status": "active",
      "client": "api",
      "files": {
        "api":       { "exists": true,  "path": "src/modules/users/users.api.ts" },
        "service":   { "exists": true,  "path": "src/modules/users/users.service.ts" },
        "types":     { "exists": true,  "path": "src/modules/users/users.types.ts" },
        "contracts": { "exists": true,  "path": "src/modules/users/contracts" },
        "hooks":     { "exists": true,  "path": "src/modules/users/hooks" }
      },
      "methods": {}
    }
  ]
}

Arquitetura Gerada

Infraestrutura Global (gerada pelo init)

src/
├── modules/
│   └── api/
│       ├── api-client.ts     ← Instâncias Axios com interceptors JWT + unwrap de response.data
│       └── api.types.ts      ← Tipos de erro: BaseError, ForbiddenError, NotFoundError, ApiError
├── types/
│   ├── global.types.ts       ← BasePayload, BaseSuccessResponse, BaseErrorResponse
│   └── shared.types.ts       ← Tipos compartilhados (ex: TFieldValidationError)
└── utils/
    └── api.utils.ts          ← parseApiError e utilitários HTTP

Por Módulo (gerado pelo module + add)

src/modules/<module>/
├── contracts/                ← Tipos específicos por operação
│   ├── <fn>.types.ts         ← Payload, Response, ErrorMap
│   └── index.ts              ← barrel (regenerado automaticamente)
├── hooks/                    ← Integração TanStack Query
│   ├── use<Fn>.hook.ts       ← useMutation (POST/PUT/DELETE) ou useQuery (GET)
│   └── index.ts              ← barrel (regenerado automaticamente)
├── <module>.api.ts           ← Objeto API com chamadas Axios tipadas
├── <module>.service.ts       ← Camada de serviço com try/catch e parseApiError
├── <module>.types.ts         ← Interface base do modelo (ex: User, Product)
└── index.ts                  ← barrel raiz (regenerado automaticamente)

Fluxo de Camadas

Componente React
     │
     ▼
hooks/useGetUser.hook.ts     → useQuery({ queryFn: ... })
     │
     ▼
users.service.ts             → try/catch + parseApiError
     │
     ▼
users.api.ts                 → api.get<GetUserResponse>(...)
     │
     ▼
api-client.ts                → Axios com Bearer token automático
     │
     ▼
Backend REST API

Exemplo de Uso no Componente

Após rodar forge add users getUser --get, você pode usar diretamente no seu componente:

import { useGetUser } from '@/modules/users/hooks/useGetUser.hook';

export function UserProfile({ id }: { id: string }) {
  const { data, isPending, isError } = useGetUser();

  if (isPending) return <Spinner />;
  if (isError) return <ErrorMessage />;

  return <div>{data.user.name}</div>;
}

O TypeScript inferirá automaticamente o tipo de data como GetUserResponse.


Roadmap

  • [x] Fase 1 — Nova interface CLI (V2 commands, aliases, modo interativo)
  • [x] Fase 2 — Generators próprios (lógica extraída para generator/)
  • [x] Fase 3 — State engine melhorado (sync V2, tipos consolidados)
  • [x] Fase 4 — forge list e suporte multi-backend (forge client)
  • [x] forge describe
  • [x] forge remove
  • [x] forge rename
  • [x] Sync detectar clients órfãos/missing no api-client.ts
  • [x] Barrel exports automáticos (index.ts)
  • [ ] Barrel exports automáticos (index.ts)
  • [ ] Suporte a WebSocket (forge ws ...)

Licença

ISC © vitongovisk