@ygorazambuja/sauron
v1.1.2
Published
OpenAPI to TypeScript/Angular Converter - Generate TypeScript interfaces and Angular HTTP services from Swagger/OpenAPI specs
Maintainers
Readme
OpenAPI to TypeScript Converter
Este projeto converte automaticamente schemas OpenAPI/Swagger JSON em definições TypeScript (interfaces e tipos).
Funcionalidades
- ✅ Interfaces TypeScript: Converte objetos OpenAPI em interfaces TypeScript
- ✅ Tipos Union: Converte enums OpenAPI em tipos union TypeScript
- ✅ Tipos Primitivos: Suporte completo a string, number, boolean, integer
- ✅ Arrays: Suporte a arrays com tipagem correta
- ✅ Propriedades Nullable: Converte
nullable: truepara| null - ✅ Referências ($ref): Resolve referências entre schemas
- ✅ Datas: Converte
format: "date-time"para tipoDate - ✅ Propriedades Obrigatórias: Todas as propriedades definidas são obrigatórias por padrão
- ✅ Nomes curtos de tipo: Extrai automaticamente o nome curto de schemas com namespace (ex:
MyApp.Core.DTOs.ProductDto→ProductDto) - ✅ Plugin system para geradores: Plugins built-in
fetch,angular,axiosemcp - ✅ Seleção explícita de plugin:
--plugin <id>(aceita múltiplos) - ✅ Compatibilidade retroativa:
--httpe--angularcontinuam funcionando como aliases - ✅ Relatório de definições ausentes: Cada plugin HTTP gera relatório com os pontos que viraram
any
Como Usar
Runtime com Node
O pacote publicado roda em Node.js 20+ e nao exige Bun no ambiente consumidor.
npx @ygorazambuja/sauron --input swagger.json --angular --http
npm install -g @ygorazambuja/sauron
sauron --input swagger.json --angular --httpOpção 1: Binário Compilado (Recomendado)
Após compilar o projeto, você pode usar o binário executável diretamente:
# Compilar o projeto
bun run build:binary
# Usar o binário
./sauron --input swagger.json --angular --httpOpção 2: Desenvolvimento com Bun
# Instalar dependências
bun install
# Criar arquivo de configuração inicial
bun run cli -- init
# Executar diretamente
bun run src/index.ts
# Ou usar o CLI wrapper
bun run cli -- --input swagger.json --angularExecutar Testes
# Executar todos os testes
bun test
# Executar testes em modo watch
bun test --watch
# Executar com cobertura
bun test --coverageO comando irá:
- Ler o arquivo OpenAPI/Swagger especificado
- Validar o schema OpenAPI
- Por padrão: Gerar apenas interfaces TypeScript (models)
- Com
--http: Gerar também métodos HTTP (fetch por padrão) - Com
--angular --http: Detectar projeto Angular e gerar serviço Angular - Com
--plugin <id>: Escolher explicitamente o plugin (fetch,angular,axios,mcp) - Com
--plugin angularfora de projeto Angular: fallback automático parafetch - Salvar nos diretórios apropriados (
outputs/ousrc/app/sauron/) - Gerar relatório de definições ausentes ao lado do cliente/serviço HTTP gerado
Flags Disponíveis
init: Criasauron.config.tscom configurações iniciais- Sem flags: Apenas models TypeScript
--http: Models + métodos HTTP com plugin padrão (fetch)--angular --http: Models + serviço Angular (alias compatível)--plugin <id>: Seleciona plugin explicitamente (fetch,angular,axios,mcp)--input arquivo.json: Especificar arquivo de entrada--output diretorio: Diretório de saída customizado--config arquivo.ts: Caminho para arquivo de configuração (padrão:sauron.config.ts)--short-names/-s: Usa nomes curtos de tipo, ex:ProductDtoem vez deMyAppCoreDTOsProductDto(padrão:true)--no-short-names: Usa nomes completos com namespace (comportamento legado)
Regras de precedência:
- Se
--pluginfor informado, ele tem prioridade sobre--httpe--angular. - Se
--pluginnão for informado,--httpusafetch. - Se
--pluginnão for informado,--http --angularusaangular.
Arquivo de Configuração (sauron.config.ts)
Você pode centralizar as opções do CLI em um arquivo:
import type { SauronConfig } from "@ygorazambuja/sauron";
export default {
input: "swagger.json",
// url: "https://api.exemplo.com/openapi.json",
// plugin: ["fetch"], // fetch | angular | axios | mcp
// shortNames: true, // Nomes curtos de tipo, ex: ProductDto em vez de MyAppCoreDTOsProductDto (padrão: true)
output: "outputs",
angular: false,
http: true,
} satisfies SauronConfig;As flags da CLI têm prioridade sobre os valores do arquivo de configuração.
Uso Programático
import {
readJsonFile,
verifySwaggerComposition,
createModels,
} from "./src/utils";
// 1. Ler arquivo JSON
const swaggerData = await readJsonFile("swagger.json");
// 2. Validar schema
const validatedSchema = verifySwaggerComposition(swaggerData);
// 3. Gerar definições TypeScript
const typeDefinitions = createModels(validatedSchema);
// Resultado: array de strings com definições TypeScript
console.log(typeDefinitions);Estrutura dos Arquivos
src/
├── index.ts # Ponto de entrada principal
├── utils/
│ └── index.ts # Funções utilitárias de conversão
└── schemas/
└── swagger.ts # Schema Zod para validação OpenAPI
outputs/
├── models/
│ └── index.ts # Arquivo gerado com tipos TypeScript
└── http-client/ # Quando plugin fetch/axios
├── sauron-api.client.ts # Cliente fetch (plugin fetch)
├── sauron-api.axios-client.ts # Cliente axios (plugin axios)
├── missing-swagger-definitions.json # Relatório (plugin fetch)
├── type-coverage-report.json # Cobertura de tipos (plugin fetch)
├── missing-swagger-definitions.axios.json # Relatório (plugin axios)
└── type-coverage-report.axios.json # Cobertura de tipos (plugin axios)
outputs/
└── mcp/ # Quando plugin mcp
├── index.ts # Entrypoint do servidor MCP (STDIO)
├── server.ts # Factory do servidor MCP
├── client/
│ └── api.client.ts # Cliente HTTP base com auth
├── tools/
│ └── *.tool.ts # Uma tool por recurso
├── types/
│ └── *.types.ts # Tipos de input/action por recurso
├── schemas/
│ └── *.schema.ts # JSON Schema por recurso
├── mcp-tools-report.json # Inventário de tools/actions geradas
└── README.md # Guia do output MCP gerado
src/app/sauron/
├── models/
│ └── index.ts # Arquivo gerado com tipos TypeScript
└── angular-http-client/ # Quando --angular --http
├── sauron-api.service.ts # Serviço Angular
├── missing-swagger-definitions.json # Relatório de definições ausentes
└── type-coverage-report.json # Cobertura de tiposExemplo de Saída
Schema OpenAPI:
{
"ProductDto": {
"type": "object",
"properties": {
"categoryId": { "type": "integer" },
"price": { "type": "number" }
}
}
}Resultado TypeScript:
export interface ProductDto {
categoryId: number;
price: number;
}API Reference
readJsonFile(filePath: string): Promise<unknown>
Lê e faz parse de um arquivo JSON do sistema de arquivos.
Parâmetros:
filePath: Caminho para o arquivo JSON
Retorna: Conteúdo JSON parseado
verifySwaggerComposition(swaggerData: Record<string, unknown>)
Valida dados OpenAPI/Swagger contra o schema esperado.
Parâmetros:
swaggerData: Dados brutos do OpenAPI JSON
Retorna: Schema validado e tipado
Lança: Error se a validação falhar
createModels(openApiSchema, options?): string[]
Gera definições TypeScript a partir de schemas OpenAPI.
Parâmetros:
openApiSchema: Schema OpenAPI validadooptions?.shortNames: Usar nomes curtos de tipo (padrão:true)
Retorna: Array de strings com definições TypeScript
Nomes Curtos de Tipo (shortNames)
Muitas APIs .NET expõem schemas com namespace completo no Swagger, como:
MyApp.Core.DTOs.ProductDto
Acme.Shared.Models.UserDtoPor padrão (shortNames: true), o Sauron extrai apenas o último segmento do nome, gerando tipos mais limpos:
| Schema no Swagger | shortNames: true (padrão) | shortNames: false (legado) |
| --- | --- | --- |
| MyApp.Core.DTOs.ProductDto | ProductDto | MyAppCoreDTOsProductDto |
| Acme.Shared.Models.UserDto | UserDto | AcmeSharedModelsUserDto |
| OrderDto | OrderDto | OrderDto |
Em caso de colisão de nomes curtos (ex: dois namespaces com mesmo sufixo UserDto), o Sauron adiciona um sufixo numérico automaticamente (UserDto, UserDto2).
Configuração
Via CLI:
# Nomes curtos (padrão)
sauron swagger.json
# Nomes completos (comportamento legado)
sauron swagger.json --no-short-namesVia arquivo de configuração:
export default {
input: "swagger.json",
shortNames: false, // usar nomes completos com namespace
} satisfies SauronConfig;Via uso programático:
import { createModels } from "@ygorazambuja/sauron";
// Nomes curtos (padrão)
const models = createModels(schema);
// Nomes completos
const models = createModels(schema, { shortNames: false });Regras de Conversão
Propriedades Obrigatórias vs Opcionais
- Por padrão: Todas as propriedades definidas no schema são obrigatórias
- Se houver lista
required: Apenas propriedades na lista são obrigatórias - Nullable: Propriedades com
nullable: truerecebem| null
Tipos Suportados
| OpenAPI | TypeScript |
| ------------------------------------ | ---------- | --- | --- |
| string | string |
| integer | number |
| number | number |
| boolean | boolean |
| string + format: "date-time" | Date |
| array + items | T[] |
| enum: [1, 2, 3] | 1 | 2 | 3 |
| $ref: "#/components/schemas/Model" | Model |
Desenvolvimento
Executar Testes
bun testModificar Schemas
Os schemas de validação estão em src/schemas/swagger.ts. Eles usam a biblioteca Zod para validação robusta.
Adicionar Novos Conversores
Para adicionar suporte a novos tipos OpenAPI, modifique a função convertSchemaToTypeScript em src/utils/index.ts.
Serviço Angular Gerado
Além dos tipos TypeScript, o projeto também gera um serviço Angular completo com métodos HTTP Client para todas as rotas da API.
Características do Serviço Gerado
- ✅ HttpClient injection: Usa o novo sistema de injeção do Angular
- ✅ Métodos tipados: Cada método tem assinatura correta de parâmetros
- ✅ Tipos de resposta: Retorna
Observable<T>com tipos específicos (quando definido no schema) - ✅ Observables: Retorna
Observable<any>quando tipo não é especificado - ✅ Parâmetros de path: Suporte a parâmetros na URL (
/users/{id}) - ✅ Query parameters: Suporte a parâmetros de query
- ✅ Request body: Suporte a POST/PUT/PATCH com body
- ✅ Nomes únicos: Evita conflitos de nomes entre métodos
- ✅ Imports automáticos: Importa apenas os tipos utilizados
Exemplo de Serviço Gerado
import { Injectable, inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { ProductPaginatedResultDto, ProductDto } from "../models";
@Injectable({
providedIn: "root"
})
export class SauronApiService {
private readonly httpClient = inject(HttpClient);
// Padrão: Get{Resource}{Modifier}
// Retorna Observable<any> quando não há schema definido
GetGenericEndpoint(): Observable<any> {
return this.httpClient.get("/api/generic/data");
}
// Retorna tipo específico quando schema está definido
GetProductWithParams(Name?: any, Page?: any, PageSize?: any, SortBy?: any, SortDirection?: any): Observable<ProductPaginatedResultDto> {
return this.httpClient.get("/api/Product/ListAll", { params: { Name, Page, PageSize, SortBy, SortDirection } });
}
// Retorna objeto único
GetProductById(id: any): Observable<ProductDto> {
return this.httpClient.get(\`/api/Product/\${id}\`);
}
// Operações CREATE/UPDATE/DELETE
PostProductCreate(body: any): Observable<ProductDto> {
return this.httpClient.post("/api/Product/Create", body);
}
PutProductUpdate(body: any): Observable<any> {
return this.httpClient.put("/api/Product/Update", body);
}
DeleteProduct(id?: any): Observable<any> {
return this.httpClient.delete("/api/Product/Delete", { params: { id } });
}
// ... mais métodos
}Tipos de Saída
Apenas Models (Padrão)
sauron swagger.jsonGera apenas interfaces TypeScript em outputs/models/.
Cliente Fetch (Vanilla JS/TS)
sauron swagger.json --httpGera models + cliente fetch-based em outputs/http-client/sauron-api.client.ts.
Também gera outputs/http-client/missing-swagger-definitions.json.
// Exemplo de uso
import { SauronApiClient } from "./outputs/http-client/sauron-api.client";
import type {
ProductDto,
ProductPaginatedResultDto,
} from "./outputs/models";
// Criar instância com base URL
const api = new SauronApiClient("https://api.exemplo.com");
// Usar métodos assíncronos com tipos específicos
try {
const products: ProductPaginatedResultDto =
await api.GetProductWithParams("search", 1, 10);
console.log("Resultados:", products);
const product: ProductDto = await api.GetProductById(123);
console.log("Produto específico:", product);
} catch (error) {
console.error("Erro na API:", error);
}Ou usar a instância padrão:
import { sauronApi } from "./outputs/http-client/sauron-api.client";
// Configura uma única vez para todas as chamadas
sauronApi.setBaseUrl("https://api.exemplo.com");
const result = await sauronApi.GetProductWithParams("search", 1, 10);Cliente Axios
sauron swagger.json --plugin axiosGera models + cliente axios em outputs/http-client/sauron-api.axios-client.ts.
Também gera outputs/http-client/missing-swagger-definitions.axios.json.
import { SauronAxiosApiClient } from "./outputs/http-client/sauron-api.axios-client";
const api = new SauronAxiosApiClient("https://api.exemplo.com");
const result = await api.GetProductWithParams("search", 1, 10);
console.log(result);Servidor MCP (STDIO)
sauron swagger.json --plugin mcpGera models + servidor MCP em outputs/mcp/index.ts.
Também gera outputs/mcp/mcp-tools-report.json.
Variáveis de ambiente suportadas no servidor gerado:
API_BASE_URL(obrigatória)API_TOKEN(opcional, Bearer token)API_KEY(opcional, API Key)API_KEY_HEADER(opcional, defaultx-api-key)
Execução:
bun run ./outputs/mcp/index.tsServiço Angular
sauron swagger.json --angular --httpGera models + serviço Angular em src/app/sauron/angular-http-client/sauron-api.service.ts.
Também gera src/app/sauron/angular-http-client/missing-swagger-definitions.json.
// Exemplo de uso
import { SauronApiService } from './src/app/sauron/angular-http-client/sauron-api.service';
constructor(private api: SauronApiService) {}
loadData() {
this.api.GetProductWithParams('search', 1, 10)
.subscribe(result => console.log(result));
}Como Usar o Serviço Gerado
import { Component, inject } from '@angular/core';
import { SauronApiService } from './outputs/angular-http-client/sauron-api.service';
import { ProductPaginatedResultDto } from './outputs/models';
@Component({...})
export class MyComponent {
private readonly api = inject(SauronApiService);
// Com tipos específicos - IntelliSense e type safety
loadProducts() {
this.api.GetProductWithParams(
'search term',
1, // Page
10, // PageSize
'Name', // SortBy
'asc' // SortDirection
).subscribe((response: ProductPaginatedResultDto) => {
// response.items é tipado como ProductDto[]
// response.totalCount é tipado como number
console.log('Total:', response.totalCount);
console.log('Items:', response.items);
});
}
// Buscar produto específico
getProduct(id: number) {
this.api.GetProductById(id).subscribe(product => {
console.log('Produto:', product);
});
}
// Criar novo produto
createProduct(productData: any) {
this.api.PostProductCreate(productData).subscribe(result => {
console.log('Criado:', result);
});
}
// Sem tipos específicos - ainda funciona
loadGenericData() {
this.api.GetGenericEndpoint().subscribe((data: any) => {
console.log(data);
});
}
}Relatório de Definições Ausentes
Quando há geração HTTP, o CLI gera automaticamente um relatório ao lado do cliente/serviço.
Arquivos atuais:
missing-swagger-definitions.json(pluginfetcheangular)missing-swagger-definitions.axios.json(pluginaxios)type-coverage-report.json(pluginfetcheangular)type-coverage-report.axios.json(pluginaxios)
Esse relatório lista os pontos da especificação Swagger/OpenAPI que resultaram em any na
camada HTTP gerada, para facilitar correções no contrato da API.
Relatório de Cobertura de Tipos
Quando há geração HTTP, o CLI também gera um relatório de cobertura de tipos por operação.
Esse relatório mostra:
- Cobertura total de tipagem (
typedvsuntyped) - Cobertura por localização (
path.parameter,query.parameter,request.body,response.body) - Resumo por operação (rota + método)
- Lista de ocorrências não tipadas em
issues
Estrutura do arquivo
generatedAt: data/hora de geração (ISO string)totalIssues: total de ocorrências encontradassummary.pathParameters: total de problemas em parâmetros de pathsummary.queryParameters: total de problemas em parâmetros de querysummary.requestBodies: total de problemas em corpos de requisiçãosummary.responseBodies: total de problemas em corpos de respostaissues: lista detalhada de ocorrências
Exemplo de item em issues
{
"path": "/api/users/{id}",
"method": "GET",
"location": "path.parameter",
"field": "id",
"reason": "Path parameter schema is missing or unresolved.",
"recommendedDefinition": "Define parameter.schema with a primitive type, enum, object, array, or valid $ref."
}Sistema de Plugins
Plugins built-in:
fetchangularaxiosmcp
Exemplos:
sauron swagger.json --plugin fetch
sauron swagger.json --plugin angular
sauron swagger.json --plugin axios
sauron swagger.json --plugin mcpPara criar novos plugins, veja docs/plugins.md.
Distribuição
Binário Executável
O projeto pode ser compilado em um único arquivo executável que roda em qualquer sistema com suporte ao Bun runtime:
# Compilar
bun run build:binary
# O arquivo 'sauron' pode ser distribuído e executado diretamente
./sauron --input api.json --angularPublicação no NPM com Bun
Para validar e publicar no registro npm:
npm pack --dry-run
npm publish --access publicEntão outros projetos podem instalar e usar:
npm install -g @ygorazambuja/sauron
sauron --input swagger.json --angularLimitações
- Não suporta todos os recursos avançados do OpenAPI 3.x
- Focado principalmente em schemas de componentes
- Não gera validações em runtime (apenas tipos TypeScript)
Licença
Este projeto é parte do sistema Sauron para conversão de APIs.
