ts-procedures 3.0.2 → 3.1.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.
@@ -1,9 +1,15 @@
1
1
  import { Hono, Context } from 'hono'
2
2
  import { kebabCase } from 'es-toolkit/string'
3
- import { Procedures, TProcedureRegistration } from '../../../index.js'
4
- import { RPCConfig, RPCHttpRouteDoc } from '../../types.js'
3
+ import { TProcedureRegistration } from '../../../index.js'
4
+ import {
5
+ ExtractConfig,
6
+ ExtractContext,
7
+ ProceduresFactory,
8
+ RPCConfig,
9
+ RPCHttpRouteDoc,
10
+ } from '../../types.js'
5
11
  import { castArray } from 'es-toolkit/compat'
6
- import { HonoFactoryItem, ExtractContext, ProceduresFactory } from './types.js'
12
+ import { HonoFactoryItem } from './types.js'
7
13
 
8
14
  export type { RPCConfig, RPCHttpRouteDoc }
9
15
 
@@ -110,7 +116,7 @@ export class HonoRPCAppBuilder {
110
116
  private factories: HonoFactoryItem<any>[] = []
111
117
 
112
118
  private _app: Hono = new Hono()
113
- private _docs: RPCHttpRouteDoc[] = []
119
+ private _docs: (RPCHttpRouteDoc & object)[] = []
114
120
 
115
121
  get app(): Hono {
116
122
  return this._app
@@ -125,14 +131,21 @@ export class HonoRPCAppBuilder {
125
131
  * @param factory - The procedure factory created by Procedures<Context, RPCConfig>()
126
132
  * @param factoryContext - The context for procedure handlers. Can be a direct value,
127
133
  * a sync function (c) => Context, or an async function (c) => Promise<Context>
134
+ * @param extendProcedureDoc - A custom function to extend the generated RPC route documentation for each procedure.
128
135
  */
129
136
  register<TFactory extends ProceduresFactory>(
130
137
  factory: TFactory,
131
138
  factoryContext:
132
139
  | ExtractContext<TFactory>
133
- | ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>)
140
+ | ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>),
141
+ extendProcedureDoc?: (params: {
142
+ /* RPC App builder base http route doc */
143
+ base: RPCHttpRouteDoc
144
+ /* Procedure registration */
145
+ procedure: TProcedureRegistration<any, ExtractConfig<TFactory>>
146
+ }) => Record<string, any>
134
147
  ): this {
135
- this.factories.push({ factory, factoryContext } as HonoFactoryItem<any>)
148
+ this.factories.push({ factory, factoryContext, extendProcedureDoc } as HonoFactoryItem<any>)
136
149
  return this
137
150
  }
138
151
 
@@ -141,9 +154,9 @@ export class HonoRPCAppBuilder {
141
154
  * @return Hono
142
155
  */
143
156
  build(): Hono {
144
- this.factories.forEach(({ factory, factoryContext }) => {
157
+ this.factories.forEach(({ factory, factoryContext, extendProcedureDoc }) => {
145
158
  factory.getProcedures().map((procedure: TProcedureRegistration<any, RPCConfig>) => {
146
- const route = this.buildRpcHttpRouteDoc(procedure)
159
+ const route = this.buildRpcHttpRouteDoc(procedure, extendProcedureDoc)
147
160
 
148
161
  this._docs.push(route)
149
162
 
@@ -182,14 +195,17 @@ export class HonoRPCAppBuilder {
182
195
  * Generates the RPC HTTP route for the given procedure.
183
196
  * @param procedure
184
197
  */
185
- private buildRpcHttpRouteDoc(procedure: TProcedureRegistration<any, RPCConfig>): RPCHttpRouteDoc {
198
+ private buildRpcHttpRouteDoc(
199
+ procedure: TProcedureRegistration<any, RPCConfig>,
200
+ extendProcedureDoc: HonoFactoryItem['extendProcedureDoc']
201
+ ): RPCHttpRouteDoc {
186
202
  const { config } = procedure
187
203
  const path = HonoRPCAppBuilder.makeRPCHttpRoutePath({
188
204
  name: procedure.name,
189
205
  config,
190
206
  prefix: this.config?.pathPrefix,
191
207
  })
192
- const method = 'post' // RPCs use POST method
208
+ const method = 'post' as const // RPCs use POST method
193
209
  const jsonSchema: { body?: object; response?: object } = {}
194
210
 
195
211
  if (config.schema?.params) {
@@ -199,10 +215,23 @@ export class HonoRPCAppBuilder {
199
215
  jsonSchema.response = config.schema.returnType
200
216
  }
201
217
 
202
- return {
218
+ const base = {
219
+ name: procedure.name,
220
+ version: config.version,
221
+ scope: config.scope,
203
222
  path,
204
223
  method,
205
224
  jsonSchema,
206
225
  }
226
+ let extendedDoc: object = {}
227
+
228
+ if (extendProcedureDoc) {
229
+ extendedDoc = extendProcedureDoc({ base, procedure })
230
+ }
231
+
232
+ return {
233
+ ...extendedDoc,
234
+ ...base,
235
+ }
207
236
  }
208
237
  }
@@ -1,33 +1,16 @@
1
- import { RPCConfig } from '../../types.js'
2
- import { Procedures } from '../../../index.js'
1
+ import { ExtractConfig, ExtractContext, RPCConfig, RPCHttpRouteDoc } from '../../types.js'
2
+ import { Procedures, TProcedureRegistration } from '../../../index.js'
3
3
  import { Context } from 'hono'
4
4
 
5
- /**
6
- * Extracts the TContext type from a Procedures factory return type.
7
- * Uses the first parameter of the handler function to infer the context type.
8
- */
9
- export type ExtractContext<TFactory> = TFactory extends {
10
- getProcedures: () => Array<{ handler: (ctx: infer TContext, ...args: any[]) => any }>
11
- }
12
- ? TContext
13
- : never
14
-
15
- /**
16
- * Minimal structural type for a Procedures factory.
17
- * Uses explicit `any` types to avoid variance issues with generic constraints.
18
- */
19
- export type ProceduresFactory = {
20
- getProcedures: () => Array<{
21
- name: string
22
- config: any
23
- handler: (ctx: any, params?: any) => Promise<any>
24
- }>
25
- Create: (...args: any[]) => any
26
- }
27
-
28
5
  export type HonoFactoryItem<TFactory = ReturnType<typeof Procedures<any, RPCConfig>>> = {
29
6
  factory: TFactory
30
7
  factoryContext:
31
8
  | ExtractContext<TFactory>
32
9
  | ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>)
10
+ extendProcedureDoc?: (params: {
11
+ /* RPC App builder base http route doc */
12
+ base: RPCHttpRouteDoc
13
+ /* Procedure registration */
14
+ procedure: TProcedureRegistration<any, ExtractConfig<TFactory>>
15
+ }) => Record<string, any>
33
16
  }
@@ -11,7 +11,8 @@ export type FactoryItem<C> = {
11
11
  factoryContext: (req: Request) => C
12
12
  }
13
13
 
14
- export interface RPCHttpRouteDoc {
14
+ export interface RPCHttpRouteDoc extends RPCConfig {
15
+ name: string // procedure name
15
16
  path: string
16
17
  method: 'post'
17
18
  jsonSchema: {
@@ -19,3 +20,40 @@ export interface RPCHttpRouteDoc {
19
20
  response?: object
20
21
  }
21
22
  }
23
+
24
+ // ================
25
+ // Utility types
26
+ // ================
27
+
28
+ /**
29
+ * Extracts the TContext type from a Procedures factory return type.
30
+ * Uses the first parameter of the handler function to infer the context type.
31
+ */
32
+ export type ExtractContext<TFactory> = TFactory extends {
33
+ getProcedures: () => Array<{ handler: (ctx: infer TContext, ...args: any[]) => any }>
34
+ }
35
+ ? TContext
36
+ : never
37
+
38
+ /**
39
+ * Extracts the TConfig type from a Procedures factory return type.
40
+ * Uses the config property of the procedure registration to infer the config type.
41
+ */
42
+ export type ExtractConfig<TFactory> = TFactory extends {
43
+ getProcedures: () => Array<{ config: infer TConfig }>
44
+ }
45
+ ? TConfig
46
+ : never
47
+
48
+ /**
49
+ * Minimal structural type for a Procedures factory.
50
+ * Uses explicit `any` types to avoid variance issues with generic constraints.
51
+ */
52
+ export type ProceduresFactory = {
53
+ getProcedures: () => Array<{
54
+ name: string
55
+ config: any
56
+ handler: (ctx: any, params?: any) => Promise<any>
57
+ }>
58
+ Create: (...args: any[]) => any
59
+ }