UNPKG

@moccona/apicodegen

Version:

A powerful OpenAPI code generator that automatically generates TypeScript API client code from OpenAPI specifications.

764 lines (763 loc) 27.2 kB
import { Block, Node, ParameterDeclaration, Statement, TypeNode } from "typescript"; import { OpenAPIV3 } from "openapi-types"; import { PluginOption } from "vite"; //#region src/core/interface.d.ts /** * Simple represenration for JSON object */ type JSONValue = { [K: string]: string | number | boolean | JSONValue | (string | number | boolean | JSONValue)[]; }; declare enum SchemaType { schemas = "schemas", parameters = "parameters", responses = "responses", requestBodies = "requestBodies" } declare enum NonArraySchemaType { object = "object", string = "string", number = "number", boolean = "boolean", integer = "integer", enum = "enum", file = "file" } declare enum ArraySchemaType { array = "array" } declare enum SchemaFormatType { string = "string", number = "number", boolean = "boolean", file = "file", binary = "binary", blob = "blob" } declare enum ParameterIn { header = "header", body = "body", query = "query", cookie = "cookie", path = "path", formData = "formData" } interface ReferenceObject { $ref: string; } interface EnumSchemaObject { name: string; enum: (string | number)[]; } interface SingleTypeSchemaObject { type: keyof typeof NonArraySchemaType | string; description?: string; allOf?: SchemaObject[]; anyOf?: SchemaObject[]; deprecated?: boolean; enum?: (string | number)[]; format?: keyof typeof SchemaFormatType; oneOf?: SchemaObject[]; properties?: Record<string, SchemaObject>; readonly?: boolean; required?: string[] | boolean; ref?: string; isRef?: boolean; } interface ArrayTypeSchemaObject { type: keyof typeof ArraySchemaType; items?: SchemaObject; required?: boolean; description?: string; ref?: string; } type SchemaObject = SingleTypeSchemaObject | ArrayTypeSchemaObject; type ParameterObject = { name: string; in: keyof typeof ParameterIn; schema?: SchemaObject; required?: boolean; description?: string; deprecated?: boolean; ref?: string; }; declare enum MediaTypes { JSON = "application/json", EVENT_STREAM = "text/event-stream", TEXT = "text", IMAGE = "image", AUDIO = "audio", VIDEO = "video" } type MediaTypeObject = { type: MediaTypes | keyof typeof MediaTypes; schema?: SchemaObject; }; type ResponsesObject = Record<string, MediaTypeObject[]>; type RequestBodyObject = ResponsesObject; declare enum HttpMethods { GET = "get", PUT = "put", POST = "post", DELETE = "delete", OPTIONS = "options", HEAD = "head", PATCH = "patch", TRACE = "trace" } type OperationObject = { method: string; summary?: string; description?: string; operationId?: string; externalDocs?: { url: string; description?: string; }[]; parameters?: ParameterObject[]; requestBody?: MediaTypeObject[]; responses: MediaTypeObject[]; deprecated?: boolean; }; type PathObject = { ref?: string; summary?: string; description?: string; parameters?: ParameterObject[]; } & Partial<Record<HttpMethods, OperationObject>>; type PathsObject = Record<string, OperationObject[]>; type FetchDocRequestInit = { method?: string; body?: string | FormData; headers?: Record<string, string>; }; declare enum Adaptors { fetch = "fetch", axios = "axios" } type ProviderInitOptions = { docURL: string; output: string; baseURL?: string; importClientSource?: string; requestOptions?: FetchDocRequestInit; verbose?: boolean; adaptor?: keyof typeof Adaptors; }; interface ProviderInitResult { readonly enums: EnumSchemaObject[]; readonly schemas: Record<string, SchemaObject>; readonly parameters: Record<string, ParameterObject>; readonly responses: Record<string, ResponsesObject>; readonly requestBodies: Record<string, RequestBodyObject>; readonly apis: PathsObject; } //#endregion //#region src/core/base/Adaptor.d.ts /** * Base adapter for tool * This abstract class serves as the foundation for implementing adapters for different code generation tools */ declare abstract class Adapter { /** * @abstract The unique name/identifier for this adapter implementation */ abstract readonly name: string; /** * @abstract The name of the field used to specify the HTTP method in API calls */ abstract readonly methodFieldName: string; /** * @abstract The name of the field used to specify the request body in API calls */ abstract readonly bodyFieldName: string; /** * @abstract The name of the field used to specify request headers in API calls */ abstract readonly headersFieldName: string; /** * @abstract The name of the field used to specify query parameters in API calls */ abstract readonly queryFieldName: string; /** * @abstract * @param {string} uri - The API endpoint URI * @param {string} method - The HTTP method (e.g., GET, POST, etc.) * @param {ParameterObject[]} parameters - An array of parameters for the API call * @param {MediaTypeObject | undefined} requestBody - The request body payload (if applicable) * @param {MediaTypeObject | undefined} response - The expected response format (if applicable) * @param {Adapter} adapter - An instance of the adapter being used * @param {boolean} useFormData - Flag indicating whether to use FormData for the request body * @param {boolean} useJSONResponse - Flag indicating whether the response should be parsed as JSON * @param {boolean} isEventStream - Flag indicating whether the response is a text/event-stream (SSE) stream; when true, the adapter must return the raw response without parsing * @returns {Statement[]} An array of TypeScript AST statements representing the generated code */ abstract client(uri: string, method: string, parameters: ParameterObject[], requestBody: MediaTypeObject | undefined, response: MediaTypeObject | undefined, adapter: Adapter, useFormData: boolean, useJSONResponse: boolean, isEventStream: boolean): Statement[]; } //#endregion //#region src/core/base/Base.d.ts /** * Represents success HTTP status codes. * Each key is a string representation of a success HTTP status code. */ declare const SuccessHttpStatusCode: { '200': string; '201': string; '202': string; '203': string; '204': string; '205': string; '206': string; '207': string; '208': string; '226': string; }; /** * Base abstract class providing common utility methods. */ declare abstract class Base { protected constructor(); /** * Converts a reference string to a meaningful name. * @param ref - The reference string to process. * @param [doc] - Optional document reference for context. * @returns - The processed name. */ static ref2name(ref: string, doc?: any): string; /** * Converts an API path to a function name. * @param path - The API endpoint path. * @param [method] - The HTTP method (e.g., GET, POST). * @param [operationId] - Unique identifier for the operation. * @returns - The generated function name. */ static pathToFnName(path: string, method?: string, _operationId?: string): string; /** * Normalizes a string by replacing special characters and avoiding TypeScript keywords. * @param text - Input text to normalize. * @returns - The normalized string. */ static normalize(text: string): string; /** * Capitalizes the first character of a string. * @param text - Input string. * @returns - Capitalized string. */ static capitalize(text: string): string; /** * Converts a string to camelCase. * @param text - Input string. * @returns - CamelCase string. */ static camelCase(text: string): string; /** * Converts a string to UpperCamelCase. * @param text - Input string. * @returns - UpperCamelCase string. */ static upperCamelCase(text: string): string; /** * Fetches documentation from a given URL. * @param url - The URL to fetch the documentation from. * @param requestInit - Additional request parameters. * @returns - A promise resolving to the fetched documentation data. */ static fetchDoc<T = unknown>(url: string, requestInit?: FetchDocRequestInit): Promise<T>; /** * Determines the media type from a given media type string. * @param mediaType - The media type string to evaluate. * @returns - The matched MediaTypes or null. */ static getMediaType(mediaType: string): MediaTypes | undefined; /** * Checks if a schema is a valid enum type that isn't boolean. * @param a - The schema object to evaluate. * @returns - True if the schema is a valid non-boolean enum. */ static isValidEnumType(a: SchemaObject): boolean; /** * Checks if a schema represents a boolean enum. * @param a - The schema object to evaluate. * @returns - True if the schema is a boolean enum. */ static isBooleanEnum(a: SchemaObject): boolean; /** * Checks if two enum schemas are identical. * @param a - First enum schema to compare. * @param b - Second enum schema to compare. * @returns - True if the enums are identical. */ private static isSameEnum; /** * Filters out duplicate enum schemas from an array. * @param enums - Array of enum schemas to process. * @returns - Array of unique enum schemas. */ static uniqueEnums(enums: EnumSchemaObject[]): EnumSchemaObject[]; /** * Finds the first occurrence of a matching enum schema in an array. * @param a - The enum schema to find. * @param enums - Array of enum schemas to search. * @returns - The found schema or undefined. */ static findSameSchema(a: EnumSchemaObject, enums: EnumSchemaObject[]): EnumSchemaObject | undefined; /** * Checks if an object is a reference object. * @param schema - The object to check. * @returns - True if the object is a reference. */ static isRef(schema: unknown): schema is ReferenceObject; } //#endregion //#region src/core/base/Provider.d.ts /** * Abstract Provider Class. * * The Provider class is designed to be extended by specific implementations (e.g., OpenAPI 2 provider, OpenAPI 3 provider). * It handles the initialization of the provider and the parsing of documentation into structured data. * * @example * * ```ts * /// Example of how this class might be used by a subclass: * class OpenAPIProvider extends Provider { * /// Implement the parse method to handle OpenAPI-specific documentation parsing. * parse(doc: unknown): ProviderInitResult { * /// Implementation details... * } * } * * /// Initializing a provider with configuration and documentation data: * const initOptions: ProviderInitOptions = { * docURL: "https://example.com/api/swagger.json", * baseURL: "https://api.example.com", * output: "./generated", * requestOptions: { * headers: { "Content-Type": "application/json" }, * }, * importClientSource: "generated/client", * }; * * const docData = fetchSwaggerDoc(); * const provider = new OpenAPIProvider(initOptions, docData); * ``` */ declare abstract class Provider implements ProviderInitResult, ProviderInitOptions { /** collection of enum schemas */ readonly enums: EnumSchemaObject[]; /** collection of schemas indexed by name */ readonly schemas: Record<string, SchemaObject>; /** collection of parameters indexed by name */ readonly parameters: Record<string, ParameterObject>; /** collection of API responses indexed by name */ readonly responses: Record<string, ResponsesObject>; /** collection of request bodies indexed by name */ readonly requestBodies: Record<string, RequestBodyObject>; /** collection of API endpoints (operations) indexed by path */ readonly apis: Record<string, OperationObject[]>; /** URL for fetching API documentation */ readonly docURL: string; /** base URL for API endpoints */ readonly baseURL: string; /** output directory for generated code */ readonly output: string; /** request options for API documentation fetch */ readonly requestOptions: FetchDocRequestInit; /** source path for imported client */ readonly importClientSource: string; /** * Provider Constructor. * @param {ProviderInitOptions} initOptions - Initial configuration for the provider. * @param {unknown} doc - Raw API documentation data to be parsed. */ constructor(initOptions: ProviderInitOptions, doc: unknown); /** * Abstract Parse Method. * @abstract * @param {unknown} doc - Raw API documentation data. * @returns {ProviderInitResult} - Parsed documentation data. * * This method must be implemented by subclasses to parse the raw documentation into structured data. */ abstract parse(doc: unknown): ProviderInitResult; } //#endregion //#region src/core/client/axios.d.ts /** * Adapter class implementing support for generating code that makes use of the Axios HTTP client library. * This class defines custom behavior and field mappings specific to the Axios client. */ declare class AxiosAdapter extends Adapter { /** * Name of the field used to specify the HTTP method in the request configuration. */ readonly methodFieldName = "method"; /** * Name of the field used to specify the request body (data) in the request configuration. */ readonly bodyFieldName = "data"; /** * Name of the field used to specify the request headers in the request configuration. */ readonly headersFieldName = "headers"; /** * Name of the field used to specify the query parameters in the request configuration. */ readonly queryFieldName = "params"; /** * The name of the client this adapter is configured for, which is 'axios' in this case. */ readonly name = "axios"; /** * Generates client code for making API requests using Axios. * @param uri - The API endpoint URI * @param method - The HTTP method (GET, POST, etc.) * @param parameters - Array of parameters to include in the request * @param requestBody - The request body media type definition * @param response - The response media type definition * @param adapter - The adapter instance * @param shouldUseFormData - Flag to use FormData for the request body * @param shouldUseJSONResponse - Unused by AxiosAdapter; present to align with the abstract signature so positional args bind correctly * @param isEventStream - Flag indicating a text/event-stream response; when true the raw AxiosResponse is returned without JSON parsing * @return - An array of generated TypeScript statements */ client(uri: string, method: string, parameters: ParameterObject[], requestBody: MediaTypeObject | undefined, response: MediaTypeObject | undefined, adapter: Adapter, shouldUseFormData: boolean, _shouldUseJSONResponse: boolean, isEventStream: boolean): Statement[]; } //#endregion //#region src/core/client/fetch.d.ts /** * FetchAdapter is an adapter class that generates client-side fetch requests. * It handles parameters, headers, and request bodies to construct proper fetch calls. */ declare class FetchAdapter extends Adapter { readonly methodFieldName = "method"; readonly bodyFieldName = "body"; readonly headersFieldName = "headers"; readonly queryFieldName = ""; readonly name = "fetch"; /** * Generates client code for making API requests using the Fetch API. * @param uri - The API endpoint URI * @param method - The HTTP method (GET, POST, etc.) * @param parameters - Array of parameters to include in the request * @param requestBody - The request body media type definition * @param response - The response media type definition * @param adapter - The adapter instance * @param shouldUseFormData - Flag to use FormData for the request body * @param shouldUseJSONResponse - Flag to use JSON parsing for the response * @param isEventStream - Flag indicating a text/event-stream response; when true the raw Response is returned unparsed * @return - An array of generated TypeScript statements */ client(uri: string, method: string, parameters: ParameterObject[], requestBody: MediaTypeObject | undefined, response: MediaTypeObject | undefined, adapter: Adapter, shouldUseFormData: boolean, shouldUseJSONResponse: boolean, isEventStream: boolean): Statement[]; } //#endregion //#region src/core/config.d.ts /** * Adaptor type for HTTP client */ type ConfigAdaptor = keyof typeof Adaptors; /** * Shared config interface for CLI and Vite plugin */ interface ApicodegenConfig { /** OpenAPI spec file path or URL (required) */ spec: string; /** Output file path */ output: string; /** HTTP client adaptor (fetch|axios) */ adaptor?: ConfigAdaptor; /** Base URL for API endpoints */ baseURL?: string; /** Custom client import source path */ importClientSource?: string; /** Enable verbose logging */ verbose?: boolean; /** Run type check after generation (default: true) */ typeCheck?: boolean; /** Watch for file changes */ watch?: boolean; /** Request options for fetching spec */ requestOptions?: FetchDocRequestInit; } /** * Options for loading config */ interface LoadConfigOptions { /** Explicit config file path */ configFile?: string; /** Config file directory (defaults to cwd) */ cwd?: string; /** CLI overrides */ cliOptions?: Partial<ApicodegenConfig>; /** Vite plugin options (for name metadata) */ name?: string; } /** * Result of config loading */ interface ResolvedConfig extends ApicodegenConfig { /** Config file path if loaded from file */ configFilePath?: string; /** Config name for logging */ name: string; } /** * Load and resolve config from multiple sources */ declare function loadConfig(options?: LoadConfigOptions): Promise<ResolvedConfig>; /** * Convert resolved config to provider options format */ declare function toProviderOptions(config: ResolvedConfig): { docURL: string; output: string; adaptor: "fetch" | "axios" | undefined; baseURL: string | undefined; importClientSource: string | undefined; verbose: boolean | undefined; requestOptions: FetchDocRequestInit | undefined; }; //#endregion //#region src/core/errors.d.ts /** * Error handling utilities for api-codegen */ declare const ErrorCodes: { readonly SPEC_NOT_FOUND: "E_SPEC_NOT_FOUND"; readonly SPEC_FETCH_FAILED: "E_SPEC_FETCH_FAILED"; readonly SPEC_PARSE_FAILED: "E_SPEC_PARSE_FAILED"; readonly OUTPUT_DIR_MISSING: "E_OUTPUT_DIR_MISSING"; readonly CONFIG_INVALID: "E_CONFIG_INVALID"; readonly VALIDATION_FAILED: "E_VALIDATION_FAILED"; readonly GENERATION_FAILED: "E_GENERATION_FAILED"; readonly TYPE_CHECK_FAILED: "E_TYPE_CHECK_FAILED"; }; type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]; /** * Error context for ApicodegenError */ interface ApicodegenErrorContext { /** Error code */ code: ErrorCode; /** Human readable message */ message: string; /** File/URL related to error */ location?: string; /** Line number if applicable */ line?: number; /** Column number if applicable */ column?: number; /** Related schema/path if applicable */ path?: string; /** Suggested fixes */ suggestions?: string[]; /** Original error */ cause?: Error; } /** * Custom error class for api-codegen with rich context */ declare class ApicodegenError extends Error { readonly code: ErrorCode; readonly location?: string; readonly line?: number; readonly column?: number; readonly path?: string; readonly suggestions: string[]; readonly cause?: Error; constructor(context: ApicodegenErrorContext); /** * Convert error to formatted string for CLI output */ toString(verbose?: boolean): string; /** * Convert to JSON-serializable object */ toJSON(): object; } /** * ANSI color codes for terminal output */ declare const Colors: { readonly reset: "\u001B[0m"; readonly bold: "\u001B[1m"; readonly red: "\u001B[31m"; readonly green: "\u001B[32m"; readonly yellow: "\u001B[33m"; readonly blue: "\u001B[34m"; readonly cyan: "\u001B[36m"; readonly gray: "\u001B[90m"; readonly brightRed: "\u001B[91m"; readonly brightGreen: "\u001B[92m"; }; /** * Format error for CLI output */ declare function formatError(error: unknown, verbose?: boolean): string; /** * Print error to console with formatting */ declare function printError(error: unknown, verbose?: boolean, stream?: NodeJS.WriteStream): void; /** * Create error with common patterns */ declare const createErrors: { specNotFound(path: string, cause?: Error): ApicodegenError; specFetchFailed(url: string, statusCode?: number, cause?: Error): ApicodegenError; specParseFailed(path: string, line?: number, column?: number, cause?: Error): ApicodegenError; outputDirMissing(path: string, cause?: Error): ApicodegenError; configInvalid(path: string, cause?: Error): ApicodegenError; validationFailed(path: string, details: string, cause?: Error): ApicodegenError; generationFailed(cause?: Error): ApicodegenError; typeCheckFailed(path: string, _errors: string[], cause?: Error): ApicodegenError; missingRequiredField(field: string, context?: string): ApicodegenError; }; /** * Wrap unknown error in ApicodegenError if needed */ declare function wrapError(error: unknown, context?: Partial<ApicodegenErrorContext>): ApicodegenError; /** * Check if error is an ApicodegenError */ declare function isApicodegenError(error: unknown): error is ApicodegenError; //#endregion //#region src/core/generator/index.d.ts /** * Represents a comment object with optional tag and message. */ type CommentObject = { tag?: 'deprecated' | 'param' | 'returns'; comment: string; paramName?: string; type?: string; }; /** * Array of comment objects to be added to the code. */ type Comments = CommentObject[]; declare class Generator { /** * Converts an array of TypeScript statements into a formatted string of code. * * @param statements - The array of TypeScript statement nodes. * @returns Formatted code as a string. * @throws {Error} If no valid statements are provided. */ static toCode(statements: Statement[]): string; static write(code: string, filepath: string): Promise<void>; /** * Converts a path string with parameters into a TypeScript template expression. * Handles query parameters and path placeholders. * * @param path - The base path string containing placeholders. * @param parameters - Array of parameter objects defining the parameters. * @param basePath - Optional base path to prepend (default: ""). * @returns A TypeScript template expressi */ static toUrlTemplate(path: string, parameters: ParameterObject[], basePath?: string): import("typescript").NoSubstitutionTemplateLiteral | import("typescript").TemplateExpression; /** * Adds synthetic comments to a TypeScript AST node. * * @param node - The target AST node. * @param comments - Array of comment objects to add. */ static addComments(node: Node, comments: Comments): void; /** * Checks if a schema represents a binary type. * * @param schema - The schema object to check. * @returns true if the schema is a binary type, false otherwise. */ static isBinarySchema(schema: SchemaObject): boolean; static schemaToTypeString(schema: SchemaObject): string; static generateParamTags(parameters: ParameterObject[], requestBody?: MediaTypeObject): CommentObject[]; static toRequestBodyTypeNode(schema: SchemaObject): ParameterDeclaration; static toTypeNode(schema: SchemaObject): TypeNode; static toDeclarationNodes(parameters: ParameterObject[]): ParameterDeclaration[]; static toFormDataStatement(parameters: ParameterObject[], requestBody?: SchemaObject): Statement[]; static bodyBlock(uri: string, method: string, parameters: ParameterObject[], requestBody: MediaTypeObject | undefined, response: MediaTypeObject | undefined, adapter: Adapter): Block; static schemaToStatemets(parsedDoc: ProviderInitResult, adaptor: Adapter, options: Omit<ProviderInitOptions, 'docURL' | 'output' | 'requestOptions'>): Statement[]; static prettier(code: string): Promise<string>; static genCode(schema: ProviderInitResult, initOptions: ProviderInitOptions, adaptor: Adapter): Promise<string>; } //#endregion //#region src/core/logger.d.ts declare const logger: { success(msg: string): void; error(err: unknown, verbose?: boolean): void; info(msg: string): void; warn(msg: string): void; loading(msg: string): void; watching(msg: string): void; fileChange(filePath: string): void; fileAdd(filePath: string): void; shutdown(): void; divider(width?: number): void; heading(text: string, mode: string, width?: number): void; item(label: string, color?: "green" | "red" | "yellow"): void; summary(stats: { succeeded: number; failed: number; endpoints: number; schemas: number; duration: number; }): void; }; //#endregion //#region src/openapi/index.d.ts declare enum OpenAPIVersion { v2 = "v2", v3 = "v3", v3_1 = "v3_1", unknown = "unknown" } declare class OpenAPIProvider extends Provider { parse(doc: OpenAPIV3.Document): ProviderInitResult; } interface CodeGenResult { code: string; stats: { endpoints: number; schemas: number; duration: number; }; } declare function codeGen(initOptions: ProviderInitOptions): Promise<CodeGenResult>; //#endregion //#region src/vite-plugin/index.d.ts type ApiCodeGenPluginOptions = { /** Human-readable name for this API config (required) */name: string; /** OpenAPI spec file path or URL */ spec?: string; /** Output file path */ output?: string; /** HTTP client adaptor */ adaptor?: 'fetch' | 'axios'; /** Base URL for API endpoints */ baseURL?: string; /** Custom client import source path */ importClientSource?: string; /** Enable verbose logging */ verbose?: boolean; /** Run type check after generation (default: true) */ typeCheck?: boolean; }; /** * Main Vite plugin function * * @example * ```ts * // vite.config.ts * import { apiCodeGenPlugin } from '@moccona/apicodegen/vite'; * * export default defineConfig({ * plugins: [ * apiCodeGenPlugin([ * { * name: 'my-api', * spec: './openapi.json', * output: './src/api/generated.ts', * baseURL: 'https://api.example.com', * }, * ]), * ], * }); * ``` */ declare function apiCodeGenPlugin(options: ApiCodeGenPluginOptions[]): PluginOption; //#endregion export { Adapter, Adaptors, ApiCodeGenPluginOptions, ApicodegenConfig, ApicodegenError, ApicodegenErrorContext, ArraySchemaType, ArrayTypeSchemaObject, AxiosAdapter, Base, CodeGenResult, Colors, CommentObject, Comments, ConfigAdaptor, EnumSchemaObject, ErrorCode, ErrorCodes, FetchAdapter, FetchDocRequestInit, Generator, HttpMethods, JSONValue, LoadConfigOptions, MediaTypeObject, MediaTypes, NonArraySchemaType, OpenAPIProvider, OpenAPIVersion, OperationObject, ParameterIn, ParameterObject, PathObject, PathsObject, Provider, ProviderInitOptions, ProviderInitResult, ReferenceObject, RequestBodyObject, ResolvedConfig, ResponsesObject, SchemaFormatType, SchemaObject, SchemaType, SingleTypeSchemaObject, SuccessHttpStatusCode, apiCodeGenPlugin, codeGen, createErrors, formatError, isApicodegenError, loadConfig, logger, printError, toProviderOptions, wrapError }; //# sourceMappingURL=index.d.mts.map