@honestjs/rpc-plugin 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,11 +32,28 @@ interface RPCPluginOptions {
32
32
  readonly tsConfigPath?: string // Path to tsconfig.json (default: 'tsconfig.json')
33
33
  readonly outputDir?: string // Output directory for generated files (default: './generated/rpc')
34
34
  readonly generateOnInit?: boolean // Generate files on initialization (default: true)
35
+ readonly openapi?: OpenApiOptions | boolean // Enable OpenAPI spec generation (default: false)
36
+ }
37
+
38
+ interface OpenApiOptions {
39
+ readonly title?: string // API title (default: 'API')
40
+ readonly version?: string // API version (default: '1.0.0')
41
+ readonly description?: string // API description (default: '')
42
+ readonly servers?: readonly { url: string; description?: string }[] // Server list
43
+ readonly outputFile?: string // Output filename (default: 'openapi.json')
35
44
  }
36
45
  ```
37
46
 
38
47
  ## What It Generates
39
48
 
49
+ The plugin generates files in the output directory (default: `./generated/rpc`):
50
+
51
+ | File | Description | When generated |
52
+ | --------------- | -------------------------------------------- | --------------------- |
53
+ | `client.ts` | Type-safe RPC client with all DTOs | Always |
54
+ | `openapi.json` | OpenAPI 3.0.3 specification | When `openapi` is set |
55
+ | `.rpc-checksum` | Hash of source files for incremental caching | Always |
56
+
40
57
  ### TypeScript RPC Client (`client.ts`)
41
58
 
42
59
  The plugin generates a single comprehensive file that includes both the client and all type definitions:
@@ -80,7 +97,7 @@ The generated `client.ts` file contains everything you need:
80
97
 
81
98
  - **ApiClient class** with all your controller methods
82
99
  - **Type definitions** for requests, responses, and DTOs
83
- - **Utility types** like RequestOptions and ApiResponse
100
+ - **Utility types** like RequestOptions
84
101
  - **Generated interfaces** from your controller types
85
102
 
86
103
  ## Custom Fetch Functions
@@ -173,6 +190,67 @@ const testApiClient = new ApiClient('http://test.com', {
173
190
  expect(mockFetch).toHaveBeenCalledWith('http://test.com/api/v1/users/123', expect.objectContaining({ method: 'GET' }))
174
191
  ```
175
192
 
193
+ ## OpenAPI Spec Generation
194
+
195
+ The plugin can produce an OpenAPI 3.0.3 JSON specification alongside the client. This is useful for Swagger UI, documentation portals, and third-party integrations. No extra dependencies are needed — the spec is built from the same route and schema data used for client generation.
196
+
197
+ ### Enable with defaults
198
+
199
+ Pass `true` to use all default values:
200
+
201
+ ```typescript
202
+ new RPCPlugin({
203
+ openapi: true
204
+ })
205
+ ```
206
+
207
+ ### Custom options
208
+
209
+ ```typescript
210
+ new RPCPlugin({
211
+ openapi: {
212
+ title: 'My Service API',
213
+ version: '2.1.0',
214
+ description: 'User management service',
215
+ servers: [
216
+ { url: 'https://api.example.com', description: 'Production' },
217
+ { url: 'http://localhost:3000', description: 'Local' }
218
+ ],
219
+ outputFile: 'api-spec.json'
220
+ }
221
+ })
222
+ ```
223
+
224
+ The generated spec includes:
225
+
226
+ - **Paths** derived from your controller routes
227
+ - **Parameters** (path, query) with correct types
228
+ - **Request bodies** referencing component schemas for DTOs
229
+ - **Responses** with schema references and array type support
230
+ - **Component schemas** extracted from `ts-json-schema-generator` output
231
+ - **Tags** derived from controller names
232
+
233
+ ## Hash-based Caching
234
+
235
+ On startup the plugin hashes all controller source files (SHA-256) and stores the checksum in `.rpc-checksum` inside the output directory. On subsequent runs, if the hash matches and the expected output files already exist, the expensive analysis and generation pipeline is skipped entirely. This significantly reduces startup time in large projects.
236
+
237
+ Caching is automatic and requires no configuration. To force regeneration:
238
+
239
+ ```typescript
240
+ // Manual call — defaults to force=true, always regenerates
241
+ await rpcPlugin.analyze()
242
+
243
+ // Explicit cache bypass
244
+ await rpcPlugin.analyze(true)
245
+
246
+ // Respect the cache (same behavior as automatic startup)
247
+ await rpcPlugin.analyze(false)
248
+ ```
249
+
250
+ You can also delete `.rpc-checksum` from the output directory to clear the cache.
251
+
252
+ > **Note:** The hash covers controller files matched by the `controllerPattern` glob. If you only change a DTO/model file that lives outside that pattern, the cache won't invalidate automatically. Use `analyze()` or delete `.rpc-checksum` in that case.
253
+
176
254
  ## How It Works
177
255
 
178
256
  ### 1. Route Analysis
@@ -196,6 +274,19 @@ expect(mockFetch).toHaveBeenCalledWith('http://test.com/api/v1/users/123', expec
196
274
  - Creates parameter validation and typing
197
275
  - Builds the complete RPC client with proper error handling
198
276
 
277
+ ### 4. OpenAPI Generation (optional)
278
+
279
+ - Converts route and schema data to OpenAPI 3.0.3 paths and components
280
+ - Maps `@Param()`, `@Query()`, and `@Body()` decorators to the correct OpenAPI parameter locations
281
+ - References component schemas via `$ref` for DTOs
282
+ - Outputs a single JSON file ready for Swagger UI or any OpenAPI-compatible tool
283
+
284
+ ### 5. Incremental Caching
285
+
286
+ - Hashes all matched controller files after glob resolution
287
+ - Compares against the stored `.rpc-checksum`
288
+ - Skips steps 1–4 when files are unchanged and output already exists
289
+
199
290
  ## Example Generated Output
200
291
 
201
292
  ### Generated Client
@@ -204,15 +295,20 @@ expect(mockFetch).toHaveBeenCalledWith('http://test.com/api/v1/users/123', expec
204
295
  export class ApiClient {
205
296
  get users() {
206
297
  return {
207
- create: async (
298
+ create: async <Result = User>(
208
299
  options: RequestOptions<{ name: string; email: string }, undefined, undefined, undefined>
209
- ): Promise<ApiResponse<any>> => {
210
- return this.request('POST', `/api/v1/users/`, options)
300
+ ) => {
301
+ return this.request<Result>('POST', `/api/v1/users/`, options)
211
302
  },
212
- list: async (
303
+ list: async <Result = User[]>(
213
304
  options?: RequestOptions<undefined, { page: number; limit: number }, undefined, undefined>
214
- ): Promise<ApiResponse<any>> => {
215
- return this.request('GET', `/api/v1/users/`, options)
305
+ ) => {
306
+ return this.request<Result>('GET', `/api/v1/users/`, options)
307
+ },
308
+ getById: async <Result = User>(
309
+ options: RequestOptions<undefined, { id: string }, undefined, undefined>
310
+ ) => {
311
+ return this.request<Result>('GET', `/api/v1/users/:id`, options)
216
312
  }
217
313
  }
218
314
  }
@@ -232,12 +328,15 @@ export type RequestOptions<
232
328
 
233
329
  ## Plugin Lifecycle
234
330
 
235
- The plugin automatically generates files when your HonestJS application starts up (if `generateOnInit` is true). You can
236
- also manually trigger generation:
331
+ The plugin automatically generates files when your HonestJS application starts up (if `generateOnInit` is true). On
332
+ subsequent startups, the hash-based cache will skip regeneration if controller files haven't changed.
333
+
334
+ You can also manually trigger generation:
237
335
 
238
336
  ```typescript
239
337
  const rpcPlugin = new RPCPlugin()
240
- await rpcPlugin.analyze() // Manually trigger analysis and generation
338
+ await rpcPlugin.analyze() // Force regeneration (bypasses cache)
339
+ await rpcPlugin.analyze(false) // Respect cache
241
340
  ```
242
341
 
243
342
  ## Advanced Usage
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { RouteInfo, ParameterMetadata, IPlugin, Application } from 'honestjs';
2
2
  import { Hono } from 'hono';
3
- import { Type } from 'ts-morph';
3
+ import { Project, Type } from 'ts-morph';
4
4
 
5
5
  /**
6
6
  * Parameter metadata with enhanced type information
@@ -9,6 +9,8 @@ interface ParameterMetadataWithType extends ParameterMetadata {
9
9
  readonly type: string;
10
10
  readonly required: boolean;
11
11
  readonly name: string;
12
+ /** Original decorator kind: 'body', 'param', 'query', 'header', etc. */
13
+ readonly decoratorType: string;
12
14
  }
13
15
  /**
14
16
  * Extended route information with comprehensive type data
@@ -49,35 +51,19 @@ interface GeneratedClientInfo {
49
51
  /**
50
52
  * Clean separation of concerns for request options
51
53
  */
52
- type RequestOptions<TParams = never, TQuery = never, TBody = never, THeaders = never> = (TParams extends never ? {
53
- params?: never;
54
- } : {
54
+ type RequestOptions<TParams = undefined, TQuery = undefined, TBody = undefined, THeaders = undefined> = (TParams extends undefined ? object : {
55
55
  params: TParams;
56
- }) & (TQuery extends never ? {
57
- query?: never;
58
- } : {
59
- query?: TQuery;
60
- }) & (TBody extends never ? {
61
- body?: never;
62
- } : {
56
+ }) & (TQuery extends undefined ? object : {
57
+ query: TQuery;
58
+ }) & (TBody extends undefined ? object : {
63
59
  body: TBody;
64
- }) & (THeaders extends never ? {
65
- headers?: never;
66
- } : {
60
+ }) & (THeaders extends undefined ? object : {
67
61
  headers: THeaders;
68
62
  });
69
63
  /**
70
64
  * Custom fetch function type that matches the standard fetch API
71
65
  */
72
66
  type FetchFunction = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
73
- /**
74
- * API Response wrapper
75
- */
76
- interface ApiResponse<T = any> {
77
- data: T;
78
- message?: string;
79
- success: boolean;
80
- }
81
67
  /**
82
68
  * API Error class
83
69
  */
@@ -86,6 +72,16 @@ declare class ApiError extends Error {
86
72
  constructor(statusCode: number, message: string);
87
73
  }
88
74
 
75
+ interface OpenApiOptions {
76
+ readonly title?: string;
77
+ readonly version?: string;
78
+ readonly description?: string;
79
+ readonly servers?: readonly {
80
+ url: string;
81
+ description?: string;
82
+ }[];
83
+ readonly outputFile?: string;
84
+ }
89
85
  /**
90
86
  * Configuration options for the RPCPlugin
91
87
  */
@@ -94,6 +90,7 @@ interface RPCPluginOptions {
94
90
  readonly tsConfigPath?: string;
95
91
  readonly outputDir?: string;
96
92
  readonly generateOnInit?: boolean;
93
+ readonly openapi?: OpenApiOptions | boolean;
97
94
  }
98
95
  /**
99
96
  * Comprehensive RPC plugin that combines route analysis, schema generation, and client generation
@@ -106,10 +103,14 @@ declare class RPCPlugin implements IPlugin {
106
103
  private readonly routeAnalyzer;
107
104
  private readonly schemaGenerator;
108
105
  private readonly clientGenerator;
106
+ private readonly openApiGenerator;
107
+ private readonly openApiOptions;
108
+ private project;
109
109
  private analyzedRoutes;
110
110
  private analyzedSchemas;
111
111
  private generatedInfo;
112
112
  constructor(options?: RPCPluginOptions);
113
+ private resolveOpenApiOptions;
113
114
  /**
114
115
  * Validates the plugin configuration
115
116
  */
@@ -123,9 +124,10 @@ declare class RPCPlugin implements IPlugin {
123
124
  */
124
125
  private analyzeEverything;
125
126
  /**
126
- * Manually trigger analysis (useful for testing or re-generation)
127
+ * Manually trigger analysis (useful for testing or re-generation).
128
+ * Defaults to force=true to bypass cache; pass false to use caching.
127
129
  */
128
- analyze(): Promise<void>;
130
+ analyze(force?: boolean): Promise<void>;
129
131
  /**
130
132
  * Get the analyzed routes
131
133
  */
@@ -138,6 +140,10 @@ declare class RPCPlugin implements IPlugin {
138
140
  * Get the generation info
139
141
  */
140
142
  getGenerationInfo(): GeneratedClientInfo | null;
143
+ /**
144
+ * Checks whether expected output files exist on disk
145
+ */
146
+ private outputFilesExist;
141
147
  /**
142
148
  * Cleanup resources to prevent memory leaks
143
149
  */
@@ -192,26 +198,50 @@ declare class ClientGeneratorService {
192
198
  private analyzeRouteParameters;
193
199
  }
194
200
 
201
+ interface ResolvedOpenApiOptions {
202
+ readonly title: string;
203
+ readonly version: string;
204
+ readonly description: string;
205
+ readonly servers: readonly {
206
+ url: string;
207
+ description?: string;
208
+ }[];
209
+ readonly outputFile: string;
210
+ }
195
211
  /**
196
- * Service for analyzing controller methods and extracting type information
212
+ * Service for generating OpenAPI 3.0.3 specifications from analyzed routes and schemas
197
213
  */
198
- declare class RouteAnalyzerService {
199
- private readonly controllerPattern;
200
- private readonly tsConfigPath;
201
- constructor(controllerPattern: string, tsConfigPath: string);
202
- private projects;
214
+ declare class OpenApiGeneratorService {
215
+ private readonly outputDir;
216
+ constructor(outputDir: string);
217
+ generateSpec(routes: readonly ExtendedRouteInfo[], schemas: readonly SchemaInfo[], options: ResolvedOpenApiOptions): Promise<string>;
218
+ private buildSpec;
219
+ private buildOperation;
220
+ private buildParameters;
221
+ private buildRequestBody;
222
+ private buildResponses;
223
+ private resolveResponseSchema;
224
+ private buildSchemaMap;
203
225
  /**
204
- * Analyzes controller methods to extract type information
226
+ * Converts Express-style `:param` path to OpenAPI `{param}` syntax
205
227
  */
206
- analyzeControllerMethods(): Promise<ExtendedRouteInfo[]>;
228
+ private toOpenApiPath;
229
+ private tsTypeToJsonSchema;
207
230
  /**
208
- * Creates a new ts-morph project
231
+ * Extracts the base type name from a TS type string, stripping
232
+ * wrappers like `Partial<...>`, `...[]`, `Promise<...>`.
209
233
  */
210
- private createProject;
234
+ private extractBaseTypeName;
235
+ }
236
+
237
+ /**
238
+ * Service for analyzing controller methods and extracting type information
239
+ */
240
+ declare class RouteAnalyzerService {
211
241
  /**
212
- * Cleanup resources to prevent memory leaks
242
+ * Analyzes controller methods to extract type information
213
243
  */
214
- dispose(): void;
244
+ analyzeControllerMethods(project: Project): Promise<ExtendedRouteInfo[]>;
215
245
  /**
216
246
  * Finds controller classes in the project
217
247
  */
@@ -241,19 +271,10 @@ declare class SchemaGeneratorService {
241
271
  private readonly controllerPattern;
242
272
  private readonly tsConfigPath;
243
273
  constructor(controllerPattern: string, tsConfigPath: string);
244
- private projects;
245
274
  /**
246
275
  * Generates JSON schemas from types used in controllers
247
276
  */
248
- generateSchemas(): Promise<SchemaInfo[]>;
249
- /**
250
- * Creates a new ts-morph project
251
- */
252
- private createProject;
253
- /**
254
- * Cleanup resources to prevent memory leaks
255
- */
256
- dispose(): void;
277
+ generateSchemas(project: Project): Promise<SchemaInfo[]>;
257
278
  /**
258
279
  * Collects types from controller files
259
280
  */
@@ -272,6 +293,26 @@ declare class SchemaGeneratorService {
272
293
  private generateSchemaForType;
273
294
  }
274
295
 
296
+ interface ChecksumData {
297
+ hash: string;
298
+ files: string[];
299
+ }
300
+ /**
301
+ * Computes a deterministic SHA-256 hash from file contents.
302
+ * Sorts paths before reading to ensure consistent ordering.
303
+ * Includes the file count in the hash so adding/removing files changes it.
304
+ */
305
+ declare function computeHash(filePaths: string[]): string;
306
+ /**
307
+ * Reads the stored checksum from the output directory.
308
+ * Returns null if the file is missing or corrupt.
309
+ */
310
+ declare function readChecksum(outputDir: string): ChecksumData | null;
311
+ /**
312
+ * Writes the checksum data to the output directory.
313
+ */
314
+ declare function writeChecksum(outputDir: string, data: ChecksumData): Promise<void>;
315
+
275
316
  /**
276
317
  * Builds the full path with parameter placeholders
277
318
  */
@@ -303,10 +344,6 @@ declare function camelCase(str: string): string;
303
344
  * Extracts a named type from a TypeScript type
304
345
  */
305
346
  declare function extractNamedType(type: Type): string | null;
306
- /**
307
- * Generates type imports for the client
308
- */
309
- declare function generateTypeImports(routes: readonly any[]): string;
310
347
 
311
348
  /**
312
349
  * Default configuration options for the RPCPlugin
@@ -334,4 +371,4 @@ declare const BUILTIN_TYPES: Set<string>;
334
371
  */
335
372
  declare const GENERIC_TYPES: Set<string>;
336
373
 
337
- export { ApiError, type ApiResponse, BUILTIN_TYPES, BUILTIN_UTILITY_TYPES, ClientGeneratorService, type ControllerGroups, DEFAULT_OPTIONS, type ExtendedRouteInfo, type FetchFunction, GENERIC_TYPES, type GeneratedClientInfo, LOG_PREFIX, type ParameterMetadataWithType, RPCPlugin, type RPCPluginOptions, type RequestOptions, RouteAnalyzerService, type RouteParameter, SchemaGeneratorService, type SchemaInfo, buildFullApiPath, buildFullPath, camelCase, extractNamedType, generateTypeImports, generateTypeScriptInterface, mapJsonSchemaTypeToTypeScript, safeToString };
374
+ export { ApiError, BUILTIN_TYPES, BUILTIN_UTILITY_TYPES, type ChecksumData, ClientGeneratorService, type ControllerGroups, DEFAULT_OPTIONS, type ExtendedRouteInfo, type FetchFunction, GENERIC_TYPES, type GeneratedClientInfo, LOG_PREFIX, OpenApiGeneratorService, type OpenApiOptions, type ParameterMetadataWithType, RPCPlugin, type RPCPluginOptions, type RequestOptions, RouteAnalyzerService, type RouteParameter, SchemaGeneratorService, type SchemaInfo, buildFullApiPath, buildFullPath, camelCase, computeHash, extractNamedType, generateTypeScriptInterface, mapJsonSchemaTypeToTypeScript, readChecksum, safeToString, writeChecksum };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { RouteInfo, ParameterMetadata, IPlugin, Application } from 'honestjs';
2
2
  import { Hono } from 'hono';
3
- import { Type } from 'ts-morph';
3
+ import { Project, Type } from 'ts-morph';
4
4
 
5
5
  /**
6
6
  * Parameter metadata with enhanced type information
@@ -9,6 +9,8 @@ interface ParameterMetadataWithType extends ParameterMetadata {
9
9
  readonly type: string;
10
10
  readonly required: boolean;
11
11
  readonly name: string;
12
+ /** Original decorator kind: 'body', 'param', 'query', 'header', etc. */
13
+ readonly decoratorType: string;
12
14
  }
13
15
  /**
14
16
  * Extended route information with comprehensive type data
@@ -49,35 +51,19 @@ interface GeneratedClientInfo {
49
51
  /**
50
52
  * Clean separation of concerns for request options
51
53
  */
52
- type RequestOptions<TParams = never, TQuery = never, TBody = never, THeaders = never> = (TParams extends never ? {
53
- params?: never;
54
- } : {
54
+ type RequestOptions<TParams = undefined, TQuery = undefined, TBody = undefined, THeaders = undefined> = (TParams extends undefined ? object : {
55
55
  params: TParams;
56
- }) & (TQuery extends never ? {
57
- query?: never;
58
- } : {
59
- query?: TQuery;
60
- }) & (TBody extends never ? {
61
- body?: never;
62
- } : {
56
+ }) & (TQuery extends undefined ? object : {
57
+ query: TQuery;
58
+ }) & (TBody extends undefined ? object : {
63
59
  body: TBody;
64
- }) & (THeaders extends never ? {
65
- headers?: never;
66
- } : {
60
+ }) & (THeaders extends undefined ? object : {
67
61
  headers: THeaders;
68
62
  });
69
63
  /**
70
64
  * Custom fetch function type that matches the standard fetch API
71
65
  */
72
66
  type FetchFunction = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
73
- /**
74
- * API Response wrapper
75
- */
76
- interface ApiResponse<T = any> {
77
- data: T;
78
- message?: string;
79
- success: boolean;
80
- }
81
67
  /**
82
68
  * API Error class
83
69
  */
@@ -86,6 +72,16 @@ declare class ApiError extends Error {
86
72
  constructor(statusCode: number, message: string);
87
73
  }
88
74
 
75
+ interface OpenApiOptions {
76
+ readonly title?: string;
77
+ readonly version?: string;
78
+ readonly description?: string;
79
+ readonly servers?: readonly {
80
+ url: string;
81
+ description?: string;
82
+ }[];
83
+ readonly outputFile?: string;
84
+ }
89
85
  /**
90
86
  * Configuration options for the RPCPlugin
91
87
  */
@@ -94,6 +90,7 @@ interface RPCPluginOptions {
94
90
  readonly tsConfigPath?: string;
95
91
  readonly outputDir?: string;
96
92
  readonly generateOnInit?: boolean;
93
+ readonly openapi?: OpenApiOptions | boolean;
97
94
  }
98
95
  /**
99
96
  * Comprehensive RPC plugin that combines route analysis, schema generation, and client generation
@@ -106,10 +103,14 @@ declare class RPCPlugin implements IPlugin {
106
103
  private readonly routeAnalyzer;
107
104
  private readonly schemaGenerator;
108
105
  private readonly clientGenerator;
106
+ private readonly openApiGenerator;
107
+ private readonly openApiOptions;
108
+ private project;
109
109
  private analyzedRoutes;
110
110
  private analyzedSchemas;
111
111
  private generatedInfo;
112
112
  constructor(options?: RPCPluginOptions);
113
+ private resolveOpenApiOptions;
113
114
  /**
114
115
  * Validates the plugin configuration
115
116
  */
@@ -123,9 +124,10 @@ declare class RPCPlugin implements IPlugin {
123
124
  */
124
125
  private analyzeEverything;
125
126
  /**
126
- * Manually trigger analysis (useful for testing or re-generation)
127
+ * Manually trigger analysis (useful for testing or re-generation).
128
+ * Defaults to force=true to bypass cache; pass false to use caching.
127
129
  */
128
- analyze(): Promise<void>;
130
+ analyze(force?: boolean): Promise<void>;
129
131
  /**
130
132
  * Get the analyzed routes
131
133
  */
@@ -138,6 +140,10 @@ declare class RPCPlugin implements IPlugin {
138
140
  * Get the generation info
139
141
  */
140
142
  getGenerationInfo(): GeneratedClientInfo | null;
143
+ /**
144
+ * Checks whether expected output files exist on disk
145
+ */
146
+ private outputFilesExist;
141
147
  /**
142
148
  * Cleanup resources to prevent memory leaks
143
149
  */
@@ -192,26 +198,50 @@ declare class ClientGeneratorService {
192
198
  private analyzeRouteParameters;
193
199
  }
194
200
 
201
+ interface ResolvedOpenApiOptions {
202
+ readonly title: string;
203
+ readonly version: string;
204
+ readonly description: string;
205
+ readonly servers: readonly {
206
+ url: string;
207
+ description?: string;
208
+ }[];
209
+ readonly outputFile: string;
210
+ }
195
211
  /**
196
- * Service for analyzing controller methods and extracting type information
212
+ * Service for generating OpenAPI 3.0.3 specifications from analyzed routes and schemas
197
213
  */
198
- declare class RouteAnalyzerService {
199
- private readonly controllerPattern;
200
- private readonly tsConfigPath;
201
- constructor(controllerPattern: string, tsConfigPath: string);
202
- private projects;
214
+ declare class OpenApiGeneratorService {
215
+ private readonly outputDir;
216
+ constructor(outputDir: string);
217
+ generateSpec(routes: readonly ExtendedRouteInfo[], schemas: readonly SchemaInfo[], options: ResolvedOpenApiOptions): Promise<string>;
218
+ private buildSpec;
219
+ private buildOperation;
220
+ private buildParameters;
221
+ private buildRequestBody;
222
+ private buildResponses;
223
+ private resolveResponseSchema;
224
+ private buildSchemaMap;
203
225
  /**
204
- * Analyzes controller methods to extract type information
226
+ * Converts Express-style `:param` path to OpenAPI `{param}` syntax
205
227
  */
206
- analyzeControllerMethods(): Promise<ExtendedRouteInfo[]>;
228
+ private toOpenApiPath;
229
+ private tsTypeToJsonSchema;
207
230
  /**
208
- * Creates a new ts-morph project
231
+ * Extracts the base type name from a TS type string, stripping
232
+ * wrappers like `Partial<...>`, `...[]`, `Promise<...>`.
209
233
  */
210
- private createProject;
234
+ private extractBaseTypeName;
235
+ }
236
+
237
+ /**
238
+ * Service for analyzing controller methods and extracting type information
239
+ */
240
+ declare class RouteAnalyzerService {
211
241
  /**
212
- * Cleanup resources to prevent memory leaks
242
+ * Analyzes controller methods to extract type information
213
243
  */
214
- dispose(): void;
244
+ analyzeControllerMethods(project: Project): Promise<ExtendedRouteInfo[]>;
215
245
  /**
216
246
  * Finds controller classes in the project
217
247
  */
@@ -241,19 +271,10 @@ declare class SchemaGeneratorService {
241
271
  private readonly controllerPattern;
242
272
  private readonly tsConfigPath;
243
273
  constructor(controllerPattern: string, tsConfigPath: string);
244
- private projects;
245
274
  /**
246
275
  * Generates JSON schemas from types used in controllers
247
276
  */
248
- generateSchemas(): Promise<SchemaInfo[]>;
249
- /**
250
- * Creates a new ts-morph project
251
- */
252
- private createProject;
253
- /**
254
- * Cleanup resources to prevent memory leaks
255
- */
256
- dispose(): void;
277
+ generateSchemas(project: Project): Promise<SchemaInfo[]>;
257
278
  /**
258
279
  * Collects types from controller files
259
280
  */
@@ -272,6 +293,26 @@ declare class SchemaGeneratorService {
272
293
  private generateSchemaForType;
273
294
  }
274
295
 
296
+ interface ChecksumData {
297
+ hash: string;
298
+ files: string[];
299
+ }
300
+ /**
301
+ * Computes a deterministic SHA-256 hash from file contents.
302
+ * Sorts paths before reading to ensure consistent ordering.
303
+ * Includes the file count in the hash so adding/removing files changes it.
304
+ */
305
+ declare function computeHash(filePaths: string[]): string;
306
+ /**
307
+ * Reads the stored checksum from the output directory.
308
+ * Returns null if the file is missing or corrupt.
309
+ */
310
+ declare function readChecksum(outputDir: string): ChecksumData | null;
311
+ /**
312
+ * Writes the checksum data to the output directory.
313
+ */
314
+ declare function writeChecksum(outputDir: string, data: ChecksumData): Promise<void>;
315
+
275
316
  /**
276
317
  * Builds the full path with parameter placeholders
277
318
  */
@@ -303,10 +344,6 @@ declare function camelCase(str: string): string;
303
344
  * Extracts a named type from a TypeScript type
304
345
  */
305
346
  declare function extractNamedType(type: Type): string | null;
306
- /**
307
- * Generates type imports for the client
308
- */
309
- declare function generateTypeImports(routes: readonly any[]): string;
310
347
 
311
348
  /**
312
349
  * Default configuration options for the RPCPlugin
@@ -334,4 +371,4 @@ declare const BUILTIN_TYPES: Set<string>;
334
371
  */
335
372
  declare const GENERIC_TYPES: Set<string>;
336
373
 
337
- export { ApiError, type ApiResponse, BUILTIN_TYPES, BUILTIN_UTILITY_TYPES, ClientGeneratorService, type ControllerGroups, DEFAULT_OPTIONS, type ExtendedRouteInfo, type FetchFunction, GENERIC_TYPES, type GeneratedClientInfo, LOG_PREFIX, type ParameterMetadataWithType, RPCPlugin, type RPCPluginOptions, type RequestOptions, RouteAnalyzerService, type RouteParameter, SchemaGeneratorService, type SchemaInfo, buildFullApiPath, buildFullPath, camelCase, extractNamedType, generateTypeImports, generateTypeScriptInterface, mapJsonSchemaTypeToTypeScript, safeToString };
374
+ export { ApiError, BUILTIN_TYPES, BUILTIN_UTILITY_TYPES, type ChecksumData, ClientGeneratorService, type ControllerGroups, DEFAULT_OPTIONS, type ExtendedRouteInfo, type FetchFunction, GENERIC_TYPES, type GeneratedClientInfo, LOG_PREFIX, OpenApiGeneratorService, type OpenApiOptions, type ParameterMetadataWithType, RPCPlugin, type RPCPluginOptions, type RequestOptions, RouteAnalyzerService, type RouteParameter, SchemaGeneratorService, type SchemaInfo, buildFullApiPath, buildFullPath, camelCase, computeHash, extractNamedType, generateTypeScriptInterface, mapJsonSchemaTypeToTypeScript, readChecksum, safeToString, writeChecksum };