@honestjs/rpc-plugin 1.6.0 → 1.6.1
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 +136 -181
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# RPC Plugin
|
|
2
2
|
|
|
3
|
-
The RPC Plugin automatically analyzes your HonestJS controllers and, by default,
|
|
4
|
-
|
|
5
|
-
can also provide custom generators.
|
|
3
|
+
The RPC Plugin automatically analyzes your HonestJS controllers and, by default, generates a fully-typed TypeScript RPC
|
|
4
|
+
client with proper parameter typing. You can also provide custom generators.
|
|
6
5
|
|
|
7
6
|
## Installation
|
|
8
7
|
|
|
@@ -17,55 +16,52 @@ pnpm add @honestjs/rpc-plugin
|
|
|
17
16
|
## Basic Setup
|
|
18
17
|
|
|
19
18
|
```typescript
|
|
20
|
-
import { RPCPlugin } from
|
|
21
|
-
import { Application } from
|
|
22
|
-
import AppModule from
|
|
19
|
+
import { RPCPlugin } from '@honestjs/rpc-plugin'
|
|
20
|
+
import { Application } from 'honestjs'
|
|
21
|
+
import AppModule from './app.module'
|
|
23
22
|
|
|
24
23
|
const { hono } = await Application.create(AppModule, {
|
|
25
|
-
plugins: [new RPCPlugin()]
|
|
26
|
-
})
|
|
24
|
+
plugins: [new RPCPlugin()]
|
|
25
|
+
})
|
|
27
26
|
|
|
28
|
-
export default hono
|
|
27
|
+
export default hono
|
|
29
28
|
```
|
|
30
29
|
|
|
31
30
|
## Configuration Options
|
|
32
31
|
|
|
33
32
|
```typescript
|
|
34
33
|
interface RPCPluginOptions {
|
|
35
|
-
readonly controllerPattern?: string
|
|
36
|
-
readonly tsConfigPath?: string
|
|
37
|
-
readonly outputDir?: string
|
|
38
|
-
readonly generateOnInit?: boolean
|
|
39
|
-
readonly generators?: readonly RPCGenerator[]
|
|
40
|
-
readonly mode?:
|
|
41
|
-
readonly logLevel?:
|
|
42
|
-
readonly customClassMatcher?: (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
readonly failOnSchemaError?: boolean; // default true in strict mode
|
|
46
|
-
readonly failOnRouteAnalysisWarning?: boolean; // default true in strict mode
|
|
34
|
+
readonly controllerPattern?: string // Glob pattern for controller files (default: 'src/modules/*/*.controller.ts')
|
|
35
|
+
readonly tsConfigPath?: string // Path to tsconfig.json (default: 'tsconfig.json')
|
|
36
|
+
readonly outputDir?: string // Output directory for generated files (default: './generated/rpc')
|
|
37
|
+
readonly generateOnInit?: boolean // Generate files on initialization (default: true)
|
|
38
|
+
readonly generators?: readonly RPCGenerator[] // Optional list of generators to execute
|
|
39
|
+
readonly mode?: 'strict' | 'best-effort' // strict fails on warnings/fallbacks
|
|
40
|
+
readonly logLevel?: 'silent' | 'error' | 'warn' | 'info' | 'debug' // default: info
|
|
41
|
+
readonly customClassMatcher?: (classDeclaration: ClassDeclaration) => boolean // optional override; default discovery uses decorators
|
|
42
|
+
readonly failOnSchemaError?: boolean // default true in strict mode
|
|
43
|
+
readonly failOnRouteAnalysisWarning?: boolean // default true in strict mode
|
|
47
44
|
readonly context?: {
|
|
48
|
-
readonly namespace?: string
|
|
45
|
+
readonly namespace?: string // Default: 'rpc'
|
|
49
46
|
readonly keys?: {
|
|
50
|
-
readonly artifact?: string
|
|
51
|
-
}
|
|
52
|
-
}
|
|
47
|
+
readonly artifact?: string // Default: 'artifact'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
53
50
|
}
|
|
54
51
|
```
|
|
55
52
|
|
|
56
53
|
### Generator Behavior
|
|
57
54
|
|
|
58
|
-
- If `generators` is omitted, the plugin uses the built-in
|
|
59
|
-
`TypeScriptClientGenerator` by default.
|
|
55
|
+
- If `generators` is omitted, the plugin uses the built-in `TypeScriptClientGenerator` by default.
|
|
60
56
|
- If `generators` is provided, only those generators are executed.
|
|
61
57
|
- You can still use the built-in TypeScript client generator explicitly:
|
|
62
58
|
|
|
63
59
|
```typescript
|
|
64
|
-
import { RPCPlugin, TypeScriptClientGenerator } from
|
|
60
|
+
import { RPCPlugin, TypeScriptClientGenerator } from '@honestjs/rpc-plugin'
|
|
65
61
|
|
|
66
62
|
new RPCPlugin({
|
|
67
|
-
generators: [new TypeScriptClientGenerator(
|
|
68
|
-
})
|
|
63
|
+
generators: [new TypeScriptClientGenerator('./generated/rpc')]
|
|
64
|
+
})
|
|
69
65
|
```
|
|
70
66
|
|
|
71
67
|
## Application Context Artifact
|
|
@@ -74,22 +70,21 @@ After analysis, RPC plugin publishes this artifact to the application context:
|
|
|
74
70
|
|
|
75
71
|
```typescript
|
|
76
72
|
type RpcArtifact = {
|
|
77
|
-
artifactVersion: string
|
|
78
|
-
routes: ExtendedRouteInfo[]
|
|
79
|
-
schemas: SchemaInfo[]
|
|
80
|
-
}
|
|
73
|
+
artifactVersion: string
|
|
74
|
+
routes: ExtendedRouteInfo[]
|
|
75
|
+
schemas: SchemaInfo[]
|
|
76
|
+
}
|
|
81
77
|
```
|
|
82
78
|
|
|
83
|
-
Default key is `'rpc.artifact'` (from
|
|
84
|
-
`context.namespace + '.' + context.keys.artifact`). This enables direct
|
|
79
|
+
Default key is `'rpc.artifact'` (from `context.namespace + '.' + context.keys.artifact`). This enables direct
|
|
85
80
|
integration with API docs:
|
|
86
81
|
|
|
87
82
|
```typescript
|
|
88
|
-
import { ApiDocsPlugin } from
|
|
83
|
+
import { ApiDocsPlugin } from '@honestjs/api-docs-plugin'
|
|
89
84
|
|
|
90
85
|
const { hono } = await Application.create(AppModule, {
|
|
91
|
-
plugins: [new RPCPlugin(), new ApiDocsPlugin({ artifact:
|
|
92
|
-
})
|
|
86
|
+
plugins: [new RPCPlugin(), new ApiDocsPlugin({ artifact: 'rpc.artifact' })]
|
|
87
|
+
})
|
|
93
88
|
```
|
|
94
89
|
|
|
95
90
|
`artifactVersion` is currently `"1"` and is used for compatibility checks.
|
|
@@ -107,45 +102,41 @@ The plugin generates files in the output directory (default: `./generated/rpc`):
|
|
|
107
102
|
|
|
108
103
|
### TypeScript RPC Client (`client.ts`)
|
|
109
104
|
|
|
110
|
-
The plugin generates a single comprehensive file that includes both the client
|
|
111
|
-
and all type definitions:
|
|
105
|
+
The plugin generates a single comprehensive file that includes both the client and all type definitions:
|
|
112
106
|
|
|
113
107
|
- **Controller-based organization**: Methods grouped by controller
|
|
114
108
|
- **Type-safe parameters**: Path, query, and body parameters with proper typing
|
|
115
|
-
- **Flexible request options**: Clean separation of params, query, body, and
|
|
116
|
-
headers
|
|
109
|
+
- **Flexible request options**: Clean separation of params, query, body, and headers
|
|
117
110
|
- **Error handling**: Built-in error handling with custom ApiError class
|
|
118
111
|
- **Header management**: Easy custom header management
|
|
119
|
-
- **Custom fetch support**: Inject custom fetch implementations for testing,
|
|
120
|
-
|
|
121
|
-
- **Integrated types**: All DTOs, interfaces, and utility types included in the
|
|
122
|
-
same file
|
|
112
|
+
- **Custom fetch support**: Inject custom fetch implementations for testing, middleware, and compatibility
|
|
113
|
+
- **Integrated types**: All DTOs, interfaces, and utility types included in the same file
|
|
123
114
|
|
|
124
115
|
```typescript
|
|
125
116
|
// Generated client usage
|
|
126
|
-
import { ApiClient } from
|
|
117
|
+
import { ApiClient } from './generated/rpc/client'
|
|
127
118
|
|
|
128
119
|
// Create client instance with base URL
|
|
129
|
-
const apiClient = new ApiClient(
|
|
120
|
+
const apiClient = new ApiClient('http://localhost:3000')
|
|
130
121
|
|
|
131
122
|
// Type-safe API calls
|
|
132
123
|
const user = await apiClient.users.create({
|
|
133
|
-
body: { name:
|
|
134
|
-
})
|
|
124
|
+
body: { name: 'John', email: 'john@example.com' }
|
|
125
|
+
})
|
|
135
126
|
|
|
136
127
|
const users = await apiClient.users.list({
|
|
137
|
-
query: { page: 1, limit: 10 }
|
|
138
|
-
})
|
|
128
|
+
query: { page: 1, limit: 10 }
|
|
129
|
+
})
|
|
139
130
|
|
|
140
131
|
const user = await apiClient.users.getById({
|
|
141
|
-
params: { id:
|
|
142
|
-
})
|
|
132
|
+
params: { id: '123' }
|
|
133
|
+
})
|
|
143
134
|
|
|
144
135
|
// Set custom headers
|
|
145
136
|
apiClient.setDefaultHeaders({
|
|
146
|
-
|
|
147
|
-
Authorization:
|
|
148
|
-
})
|
|
137
|
+
'X-API-Key': 'your-api-key',
|
|
138
|
+
Authorization: 'Bearer your-jwt-token'
|
|
139
|
+
})
|
|
149
140
|
```
|
|
150
141
|
|
|
151
142
|
The generated `client.ts` file contains everything you need:
|
|
@@ -161,8 +152,7 @@ The RPC client supports custom fetch implementations, which is useful for:
|
|
|
161
152
|
|
|
162
153
|
- **Testing**: Inject mock fetch functions for unit testing
|
|
163
154
|
- **Custom Logic**: Add logging, retries, or other middleware
|
|
164
|
-
- **Environment Compatibility**: Use different fetch implementations
|
|
165
|
-
(node-fetch, undici, etc.)
|
|
155
|
+
- **Environment Compatibility**: Use different fetch implementations (node-fetch, undici, etc.)
|
|
166
156
|
- **Interceptors**: Wrap requests with custom logic before/after execution
|
|
167
157
|
|
|
168
158
|
### Basic Custom Fetch Example
|
|
@@ -170,18 +160,13 @@ The RPC client supports custom fetch implementations, which is useful for:
|
|
|
170
160
|
```typescript
|
|
171
161
|
// Simple logging wrapper
|
|
172
162
|
const loggingFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
173
|
-
console.log(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const apiClient = new ApiClient("http://localhost:3000", {
|
|
183
|
-
fetchFn: loggingFetch,
|
|
184
|
-
});
|
|
163
|
+
console.log(`[${new Date().toISOString()}] Making ${init?.method || 'GET'} request to:`, input)
|
|
164
|
+
return fetch(input, init)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const apiClient = new ApiClient('http://localhost:3000', {
|
|
168
|
+
fetchFn: loggingFetch
|
|
169
|
+
})
|
|
185
170
|
```
|
|
186
171
|
|
|
187
172
|
### Advanced Custom Fetch Examples
|
|
@@ -192,26 +177,24 @@ const retryFetch = (maxRetries = 3) => {
|
|
|
192
177
|
return async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
193
178
|
for (let i = 0; i <= maxRetries; i++) {
|
|
194
179
|
try {
|
|
195
|
-
const response = await fetch(input, init)
|
|
196
|
-
if (response.ok) return response
|
|
180
|
+
const response = await fetch(input, init)
|
|
181
|
+
if (response.ok) return response
|
|
197
182
|
|
|
198
|
-
if (i === maxRetries) return response
|
|
183
|
+
if (i === maxRetries) return response
|
|
199
184
|
|
|
200
185
|
// Wait with exponential backoff
|
|
201
|
-
await new Promise((resolve) =>
|
|
202
|
-
setTimeout(resolve, Math.pow(2, i) * 1000)
|
|
203
|
-
);
|
|
186
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000))
|
|
204
187
|
} catch (error) {
|
|
205
|
-
if (i === maxRetries) throw error
|
|
188
|
+
if (i === maxRetries) throw error
|
|
206
189
|
}
|
|
207
190
|
}
|
|
208
|
-
throw new Error(
|
|
209
|
-
}
|
|
210
|
-
}
|
|
191
|
+
throw new Error('Max retries exceeded')
|
|
192
|
+
}
|
|
193
|
+
}
|
|
211
194
|
|
|
212
|
-
const apiClientWithRetry = new ApiClient(
|
|
213
|
-
fetchFn: retryFetch(3)
|
|
214
|
-
})
|
|
195
|
+
const apiClientWithRetry = new ApiClient('http://localhost:3000', {
|
|
196
|
+
fetchFn: retryFetch(3)
|
|
197
|
+
})
|
|
215
198
|
|
|
216
199
|
// Request/response interceptor
|
|
217
200
|
const interceptorFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
@@ -220,20 +203,20 @@ const interceptorFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
|
220
203
|
...init,
|
|
221
204
|
headers: {
|
|
222
205
|
...init?.headers,
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
}
|
|
206
|
+
'X-Request-ID': crypto.randomUUID()
|
|
207
|
+
}
|
|
208
|
+
}
|
|
226
209
|
|
|
227
210
|
return fetch(input, enhancedInit).then((response) => {
|
|
228
211
|
// Post-response logic
|
|
229
|
-
console.log(`Response status: ${response.status}`)
|
|
230
|
-
return response
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const apiClientWithInterceptor = new ApiClient(
|
|
235
|
-
fetchFn: interceptorFetch
|
|
236
|
-
})
|
|
212
|
+
console.log(`Response status: ${response.status}`)
|
|
213
|
+
return response
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const apiClientWithInterceptor = new ApiClient('http://localhost:3000', {
|
|
218
|
+
fetchFn: interceptorFetch
|
|
219
|
+
})
|
|
237
220
|
```
|
|
238
221
|
|
|
239
222
|
### Testing with Custom Fetch
|
|
@@ -242,44 +225,37 @@ const apiClientWithInterceptor = new ApiClient("http://localhost:3000", {
|
|
|
242
225
|
// Mock fetch for testing
|
|
243
226
|
const mockFetch = jest.fn().mockResolvedValue({
|
|
244
227
|
ok: true,
|
|
245
|
-
json: () => Promise.resolve({ data: { id:
|
|
246
|
-
})
|
|
228
|
+
json: () => Promise.resolve({ data: { id: '123', name: 'Test User' } })
|
|
229
|
+
})
|
|
247
230
|
|
|
248
|
-
const testApiClient = new ApiClient(
|
|
249
|
-
fetchFn: mockFetch
|
|
250
|
-
})
|
|
231
|
+
const testApiClient = new ApiClient('http://test.com', {
|
|
232
|
+
fetchFn: mockFetch
|
|
233
|
+
})
|
|
251
234
|
|
|
252
235
|
// Your test can now verify the mock was called
|
|
253
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
254
|
-
"http://test.com/api/v1/users/123",
|
|
255
|
-
expect.objectContaining({ method: "GET" }),
|
|
256
|
-
);
|
|
236
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.com/api/v1/users/123', expect.objectContaining({ method: 'GET' }))
|
|
257
237
|
```
|
|
258
238
|
|
|
259
239
|
## Hash-based Caching
|
|
260
240
|
|
|
261
|
-
On startup the plugin hashes all controller source files (SHA-256) and stores
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
analysis and generation pipeline is skipped entirely. This significantly reduces
|
|
265
|
-
startup time in large projects.
|
|
241
|
+
On startup the plugin hashes all controller source files (SHA-256) and stores the checksum in `.rpc-checksum` inside the
|
|
242
|
+
output directory. On subsequent runs, if the hash matches and the expected output files already exist, the expensive
|
|
243
|
+
analysis and generation pipeline is skipped entirely. This significantly reduces startup time in large projects.
|
|
266
244
|
|
|
267
245
|
Caching is automatic and requires no configuration. To force regeneration:
|
|
268
246
|
|
|
269
247
|
```typescript
|
|
270
248
|
// Explicit cache bypass
|
|
271
|
-
await rpcPlugin.analyze({ force: true })
|
|
249
|
+
await rpcPlugin.analyze({ force: true })
|
|
272
250
|
|
|
273
251
|
// Respect the cache (same behavior as automatic startup)
|
|
274
|
-
await rpcPlugin.analyze({ force: false })
|
|
252
|
+
await rpcPlugin.analyze({ force: false })
|
|
275
253
|
```
|
|
276
254
|
|
|
277
|
-
You can also delete `.rpc-checksum` from the output directory to clear the
|
|
278
|
-
cache.
|
|
255
|
+
You can also delete `.rpc-checksum` from the output directory to clear the cache.
|
|
279
256
|
|
|
280
|
-
> **Note:** The hash covers controller files matched by the `controllerPattern`
|
|
281
|
-
>
|
|
282
|
-
> cache won't invalidate automatically. Use `analyze()` or delete
|
|
257
|
+
> **Note:** The hash covers controller files matched by the `controllerPattern` glob. If you only change a DTO/model
|
|
258
|
+
> file that lives outside that pattern, the cache won't invalidate automatically. Use `analyze()` or delete
|
|
283
259
|
> `.rpc-checksum` in that case.
|
|
284
260
|
|
|
285
261
|
## How It Works
|
|
@@ -320,40 +296,21 @@ export class ApiClient {
|
|
|
320
296
|
get users() {
|
|
321
297
|
return {
|
|
322
298
|
create: async <Result = User>(
|
|
323
|
-
options: RequestOptions<
|
|
324
|
-
{ name: string; email: string },
|
|
325
|
-
undefined,
|
|
326
|
-
undefined,
|
|
327
|
-
undefined
|
|
328
|
-
>,
|
|
299
|
+
options: RequestOptions<{ name: string; email: string }, undefined, undefined, undefined>
|
|
329
300
|
) => {
|
|
330
|
-
return this.request<Result>(
|
|
301
|
+
return this.request<Result>('POST', `/api/v1/users/`, options)
|
|
331
302
|
},
|
|
332
303
|
list: async <Result = User[]>(
|
|
333
|
-
options?: RequestOptions<
|
|
334
|
-
undefined,
|
|
335
|
-
{ page: number; limit: number },
|
|
336
|
-
undefined,
|
|
337
|
-
undefined
|
|
338
|
-
>,
|
|
304
|
+
options?: RequestOptions<undefined, { page: number; limit: number }, undefined, undefined>
|
|
339
305
|
) => {
|
|
340
|
-
return this.request<Result>(
|
|
306
|
+
return this.request<Result>('GET', `/api/v1/users/`, options)
|
|
341
307
|
},
|
|
342
308
|
getById: async <Result = User>(
|
|
343
|
-
options: RequestOptions<
|
|
344
|
-
undefined,
|
|
345
|
-
{ id: string },
|
|
346
|
-
undefined,
|
|
347
|
-
undefined
|
|
348
|
-
>,
|
|
309
|
+
options: RequestOptions<undefined, { id: string }, undefined, undefined>
|
|
349
310
|
) => {
|
|
350
|
-
return this.request<Result>(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
options,
|
|
354
|
-
);
|
|
355
|
-
},
|
|
356
|
-
};
|
|
311
|
+
return this.request<Result>('GET', `/api/v1/users/:id`, options)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
357
314
|
}
|
|
358
315
|
}
|
|
359
316
|
|
|
@@ -362,29 +319,27 @@ export type RequestOptions<
|
|
|
362
319
|
TParams = undefined,
|
|
363
320
|
TQuery = undefined,
|
|
364
321
|
TBody = undefined,
|
|
365
|
-
THeaders = undefined
|
|
366
|
-
> =
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
& (THeaders extends undefined ? object : { headers: THeaders });
|
|
322
|
+
THeaders = undefined
|
|
323
|
+
> = (TParams extends undefined ? object : { params: TParams }) &
|
|
324
|
+
(TQuery extends undefined ? object : { query: TQuery }) &
|
|
325
|
+
(TBody extends undefined ? object : { body: TBody }) &
|
|
326
|
+
(THeaders extends undefined ? object : { headers: THeaders })
|
|
371
327
|
```
|
|
372
328
|
|
|
373
329
|
## Plugin Lifecycle
|
|
374
330
|
|
|
375
|
-
The plugin automatically generates files when your HonestJS application starts
|
|
376
|
-
|
|
377
|
-
will skip regeneration if controller files haven't changed.
|
|
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.
|
|
378
333
|
|
|
379
334
|
You can also manually trigger generation:
|
|
380
335
|
|
|
381
336
|
```typescript
|
|
382
|
-
const rpcPlugin = new RPCPlugin()
|
|
383
|
-
await rpcPlugin.analyze({ force: true })
|
|
384
|
-
await rpcPlugin.analyze({ force: false })
|
|
337
|
+
const rpcPlugin = new RPCPlugin()
|
|
338
|
+
await rpcPlugin.analyze({ force: true }) // Force regeneration (bypasses cache)
|
|
339
|
+
await rpcPlugin.analyze({ force: false }) // Respect cache
|
|
385
340
|
|
|
386
341
|
// Analyze-only mode (no files generated, diagnostics still emitted)
|
|
387
|
-
await rpcPlugin.analyze({ force: true, dryRun: true })
|
|
342
|
+
await rpcPlugin.analyze({ force: true, dryRun: true })
|
|
388
343
|
```
|
|
389
344
|
|
|
390
345
|
## Advanced Usage
|
|
@@ -395,9 +350,9 @@ If your controllers follow a different file structure:
|
|
|
395
350
|
|
|
396
351
|
```typescript
|
|
397
352
|
new RPCPlugin({
|
|
398
|
-
controllerPattern:
|
|
399
|
-
outputDir:
|
|
400
|
-
})
|
|
353
|
+
controllerPattern: 'src/controllers/**/*.controller.ts',
|
|
354
|
+
outputDir: './src/generated/api'
|
|
355
|
+
})
|
|
401
356
|
```
|
|
402
357
|
|
|
403
358
|
### Manual Generation Control
|
|
@@ -406,11 +361,11 @@ Disable automatic generation and control when files are generated:
|
|
|
406
361
|
|
|
407
362
|
```typescript
|
|
408
363
|
const rpcPlugin = new RPCPlugin({
|
|
409
|
-
generateOnInit: false
|
|
410
|
-
})
|
|
364
|
+
generateOnInit: false
|
|
365
|
+
})
|
|
411
366
|
|
|
412
367
|
// Later in your code
|
|
413
|
-
await rpcPlugin.analyze()
|
|
368
|
+
await rpcPlugin.analyze()
|
|
414
369
|
```
|
|
415
370
|
|
|
416
371
|
## Integration with HonestJS
|
|
@@ -420,32 +375,32 @@ await rpcPlugin.analyze();
|
|
|
420
375
|
Here's how your controllers should be structured for optimal RPC generation:
|
|
421
376
|
|
|
422
377
|
```typescript
|
|
423
|
-
import { Body, Controller, Get, Param, Post, Query } from
|
|
378
|
+
import { Body, Controller, Get, Param, Post, Query } from 'honestjs'
|
|
424
379
|
|
|
425
380
|
interface CreateUserDto {
|
|
426
|
-
name: string
|
|
427
|
-
email: string
|
|
381
|
+
name: string
|
|
382
|
+
email: string
|
|
428
383
|
}
|
|
429
384
|
|
|
430
385
|
interface ListUsersQuery {
|
|
431
|
-
page?: number
|
|
432
|
-
limit?: number
|
|
386
|
+
page?: number
|
|
387
|
+
limit?: number
|
|
433
388
|
}
|
|
434
389
|
|
|
435
|
-
@Controller(
|
|
390
|
+
@Controller('/users')
|
|
436
391
|
export class UsersController {
|
|
437
|
-
@Post(
|
|
392
|
+
@Post('/')
|
|
438
393
|
async create(@Body() createUserDto: CreateUserDto): Promise<User> {
|
|
439
394
|
// Implementation
|
|
440
395
|
}
|
|
441
396
|
|
|
442
|
-
@Get(
|
|
397
|
+
@Get('/')
|
|
443
398
|
async list(@Query() query: ListUsersQuery): Promise<User[]> {
|
|
444
399
|
// Implementation
|
|
445
400
|
}
|
|
446
401
|
|
|
447
|
-
@Get(
|
|
448
|
-
async getById(@Param(
|
|
402
|
+
@Get('/:id')
|
|
403
|
+
async getById(@Param('id') id: string): Promise<User> {
|
|
449
404
|
// Implementation
|
|
450
405
|
}
|
|
451
406
|
}
|
|
@@ -456,13 +411,13 @@ export class UsersController {
|
|
|
456
411
|
Ensure your controllers are properly registered in modules:
|
|
457
412
|
|
|
458
413
|
```typescript
|
|
459
|
-
import { Module } from
|
|
460
|
-
import { UsersController } from
|
|
461
|
-
import { UsersService } from
|
|
414
|
+
import { Module } from 'honestjs'
|
|
415
|
+
import { UsersController } from './users.controller'
|
|
416
|
+
import { UsersService } from './users.service'
|
|
462
417
|
|
|
463
418
|
@Module({
|
|
464
419
|
controllers: [UsersController],
|
|
465
|
-
services: [UsersService]
|
|
420
|
+
services: [UsersService]
|
|
466
421
|
})
|
|
467
422
|
export class UsersModule {}
|
|
468
423
|
```
|
|
@@ -474,13 +429,13 @@ The generated client includes comprehensive error handling:
|
|
|
474
429
|
```typescript
|
|
475
430
|
try {
|
|
476
431
|
const user = await apiClient.users.create({
|
|
477
|
-
body: { name:
|
|
478
|
-
})
|
|
432
|
+
body: { name: 'John', email: 'john@example.com' }
|
|
433
|
+
})
|
|
479
434
|
} catch (error) {
|
|
480
435
|
if (error instanceof ApiError) {
|
|
481
|
-
console.error(`API Error ${error.statusCode}: ${error.message}`)
|
|
436
|
+
console.error(`API Error ${error.statusCode}: ${error.message}`)
|
|
482
437
|
} else {
|
|
483
|
-
console.error(
|
|
438
|
+
console.error('Unexpected error:', error)
|
|
484
439
|
}
|
|
485
440
|
}
|
|
486
441
|
```
|
package/dist/index.js
CHANGED
|
@@ -1030,7 +1030,9 @@ var RPCPlugin = class {
|
|
|
1030
1030
|
this.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project);
|
|
1031
1031
|
warnings.push(...this.schemaGenerator.getWarnings());
|
|
1032
1032
|
if (this.failOnRouteAnalysisWarning && this.routeAnalyzer.getWarnings().length > 0) {
|
|
1033
|
-
throw new Error(
|
|
1033
|
+
throw new Error(
|
|
1034
|
+
`Route analysis warnings encountered in strict mode: ${this.routeAnalyzer.getWarnings().join("; ")}`
|
|
1035
|
+
);
|
|
1034
1036
|
}
|
|
1035
1037
|
if (!dryRun) {
|
|
1036
1038
|
this.generatedInfos = await this.runGenerators();
|