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.2.4

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
├── hooks/
│   ├── useCreateUser.hook.ts
│   ├── useDeleteUser.hook.ts
│   ├── useGetUser.hook.ts
│   └── useUpdateUser.hook.ts
├── users.api.ts
├── users.service.ts
└── users.types.ts

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)
  • Exibe um relatório detalhado no terminal

Relatório de exemplo:

🔄 Sincronizando projeto...

✅ Sync finalizado!

📊 Módulos: 2 ativos, 1 incompletos, 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.

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).


Comandos em Desenvolvimento 🚧

Os comandos abaixo já estão registrados no CLI e serão implementados em breve:

| Comando | Alias | Descrição | |---|---|---| | forge remove <module> <fn> | forge rm | Remove um endpoint e seus arquivos gerados | | forge rename <module> <old> <new> | forge rn | Renomeia um endpoint em todas as camadas | | forge describe <module> | forge d | Exibe detalhes de um módulo (endpoints, hooks, contratos) |


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
├── hooks/                    ← Integração TanStack Query
│   └── use<Fn>.hook.ts       ← useMutation (POST/PUT/DELETE) ou useQuery (GET)
├── <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)

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)
  • [ ] forge remove e forge rename
  • [ ] forge describe
  • [ ] Sync detectar clients órfãos/missing no api-client.ts
  • [ ] Barrel exports automáticos (index.ts)
  • [ ] Suporte a WebSocket (forge ws ...)

Licença

ISC © vitongovisk