ts-procedures 5.2.0 → 5.4.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.
Files changed (49) hide show
  1. package/README.md +150 -0
  2. package/agent_config/bin/postinstall.mjs +105 -0
  3. package/agent_config/bin/setup.mjs +286 -0
  4. package/agent_config/claude-code/.claude-plugin/plugin.json +5 -0
  5. package/agent_config/claude-code/agents/ts-procedures-architect.md +188 -0
  6. package/agent_config/claude-code/skills/guide/SKILL.md +142 -0
  7. package/agent_config/claude-code/skills/guide/anti-patterns.md +608 -0
  8. package/agent_config/claude-code/skills/guide/api-reference.md +696 -0
  9. package/agent_config/claude-code/skills/guide/patterns.md +727 -0
  10. package/agent_config/claude-code/skills/review/SKILL.md +53 -0
  11. package/agent_config/claude-code/skills/review/checklist.md +163 -0
  12. package/agent_config/claude-code/skills/scaffold/SKILL.md +56 -0
  13. package/agent_config/claude-code/skills/scaffold/templates/express-rpc.md +134 -0
  14. package/agent_config/claude-code/skills/scaffold/templates/hono-api.md +169 -0
  15. package/agent_config/claude-code/skills/scaffold/templates/hono-rpc.md +139 -0
  16. package/agent_config/claude-code/skills/scaffold/templates/hono-stream.md +134 -0
  17. package/agent_config/claude-code/skills/scaffold/templates/procedure.md +77 -0
  18. package/agent_config/claude-code/skills/scaffold/templates/stream-procedure.md +113 -0
  19. package/agent_config/copilot/copilot-instructions.md +290 -0
  20. package/agent_config/cursor/cursorrules +290 -0
  21. package/agent_config/lib/install-claude.mjs +109 -0
  22. package/build/implementations/http/hono-api/index.d.ts +102 -0
  23. package/build/implementations/http/hono-api/index.js +339 -0
  24. package/build/implementations/http/hono-api/index.js.map +1 -0
  25. package/build/implementations/http/hono-api/index.test.d.ts +1 -0
  26. package/build/implementations/http/hono-api/index.test.js +983 -0
  27. package/build/implementations/http/hono-api/index.test.js.map +1 -0
  28. package/build/implementations/http/hono-api/types.d.ts +13 -0
  29. package/build/implementations/http/hono-api/types.js +2 -0
  30. package/build/implementations/http/hono-api/types.js.map +1 -0
  31. package/build/implementations/types.d.ts +44 -0
  32. package/build/index.d.ts +28 -6
  33. package/build/index.js +28 -0
  34. package/build/index.js.map +1 -1
  35. package/build/schema/compute-schema.d.ts +5 -0
  36. package/build/schema/compute-schema.js +8 -1
  37. package/build/schema/compute-schema.js.map +1 -1
  38. package/build/schema/parser.d.ts +6 -5
  39. package/build/schema/parser.js +54 -0
  40. package/build/schema/parser.js.map +1 -1
  41. package/package.json +14 -4
  42. package/src/implementations/http/README.md +45 -2
  43. package/src/implementations/http/hono-api/index.test.ts +1328 -0
  44. package/src/implementations/http/hono-api/index.ts +461 -0
  45. package/src/implementations/http/hono-api/types.ts +16 -0
  46. package/src/implementations/types.ts +52 -0
  47. package/src/index.ts +87 -10
  48. package/src/schema/compute-schema.ts +23 -2
  49. package/src/schema/parser.ts +70 -3
@@ -0,0 +1,696 @@
1
+ # ts-procedures API Reference
2
+
3
+ ## Imports
4
+
5
+ ```typescript
6
+ // Core
7
+ import { Procedures } from 'ts-procedures'
8
+ import type {
9
+ TLocalContext,
10
+ TStreamContext,
11
+ TProcedureRegistration,
12
+ TStreamProcedureRegistration,
13
+ } from 'ts-procedures'
14
+
15
+ // Errors
16
+ import {
17
+ ProcedureError,
18
+ ProcedureValidationError,
19
+ ProcedureYieldValidationError,
20
+ ProcedureRegistrationError,
21
+ } from 'ts-procedures'
22
+
23
+ // Schema utilities
24
+ import { extractJsonSchema } from 'ts-procedures'
25
+ import { schemaParser } from 'ts-procedures'
26
+ import { isTypeboxSchema } from 'ts-procedures'
27
+ import type { TSchemaLib, TSchemaLibGenerator, TJSONSchema, TSchemaParsed, TSchemaValidationError } from 'ts-procedures'
28
+
29
+ // Stack utilities
30
+ import { captureDefinitionInfo, formatDefinitionInfo } from 'ts-procedures'
31
+ import type { DefinitionLocation, DefinitionInfo } from 'ts-procedures'
32
+
33
+ // HTTP types (types only, no runtime)
34
+ import type { RPCConfig, RPCHttpRouteDoc, StreamHttpRouteDoc, StreamMode, APIConfig, APIHttpRouteDoc, APIInput, HttpMethod } from 'ts-procedures/http'
35
+
36
+ // Express RPC
37
+ import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
38
+
39
+ // Hono RPC
40
+ import { HonoRPCAppBuilder } from 'ts-procedures/hono-rpc'
41
+
42
+ // Hono Streaming
43
+ import { HonoStreamAppBuilder, sse } from 'ts-procedures/hono-stream'
44
+
45
+ // Hono API (REST-style)
46
+ import { HonoAPIAppBuilder } from 'ts-procedures/hono-api'
47
+ import type { APIConfig, APIHttpRouteDoc, APIInput, HttpMethod } from 'ts-procedures/hono-api'
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Procedures\<TContext, TExtendedConfig\>(builder?)
53
+
54
+ Factory function that creates a scoped procedure registration system.
55
+
56
+ ```typescript
57
+ function Procedures<TContext = unknown, TExtendedConfig = unknown>(
58
+ builder?: {
59
+ onCreate?: (procedure: {
60
+ name: string
61
+ isStream?: boolean
62
+ handler: Function
63
+ config: { description?: string; schema?: object } & TExtendedConfig
64
+ }) => void
65
+ }
66
+ ): {
67
+ Create: CreateFunction
68
+ CreateStream: CreateStreamFunction
69
+ getProcedures(): Array<TProcedureRegistration | TStreamProcedureRegistration>
70
+ getProcedure(name: string): TProcedureRegistration | TStreamProcedureRegistration | undefined
71
+ removeProcedure(name: string): boolean
72
+ clear(): void
73
+ }
74
+ ```
75
+
76
+ ### Parameters
77
+
78
+ - `builder.onCreate` — Called each time a procedure is registered. Use for framework integration, route registration, or logging.
79
+
80
+ ### Return Value
81
+
82
+ | Method | Description |
83
+ |--------|-------------|
84
+ | `Create(name, config, handler)` | Register a standard async procedure |
85
+ | `CreateStream(name, config, handler)` | Register a streaming async generator procedure |
86
+ | `getProcedures()` | Returns array of all registered procedure metadata |
87
+ | `getProcedure(name)` | Returns single procedure by name, or `undefined` |
88
+ | `removeProcedure(name)` | Removes procedure, returns `true` if found |
89
+ | `clear()` | Removes all registered procedures |
90
+
91
+ ### Type Parameters
92
+
93
+ - `TContext` — Base context type injected into every handler. Merged with `TLocalContext` (adds `error()` and optional `signal`).
94
+ - `TExtendedConfig` — Additional config fields required on every procedure's config object (e.g., `RPCConfig` adds `scope` and `version`).
95
+
96
+ ---
97
+
98
+ ## Create\<TName, TParams, TReturnType\>(name, config, handler)
99
+
100
+ Registers a standard async procedure.
101
+
102
+ ```typescript
103
+ function Create<TName extends string, TParams, TReturnType>(
104
+ name: TName,
105
+ config: {
106
+ description?: string
107
+ schema?: {
108
+ params?: TParams // TypeBox schema — validated at runtime
109
+ returnType?: TReturnType // Documentation only — NOT validated
110
+ }
111
+ } & TExtendedConfig,
112
+ handler: (
113
+ ctx: TContext & TLocalContext & { isPrevalidated?: boolean },
114
+ params: TSchemaLib<TParams>
115
+ ) => Promise<TSchemaLib<TReturnType>>
116
+ ): {
117
+ [K in TName]: (ctx: TContext, params: TSchemaLib<TParams>) => Promise<TSchemaLib<TReturnType>>
118
+ procedure: (ctx: TContext, params: TSchemaLib<TParams>) => Promise<TSchemaLib<TReturnType>>
119
+ info: {
120
+ name: TName
121
+ description?: string
122
+ schema: { params?: TJSONSchema; returnType?: TJSONSchema }
123
+ validation?: { params?: (params: any) => { errors?: TSchemaValidationError[] } }
124
+ } & TExtendedConfig
125
+ }
126
+ ```
127
+
128
+ ### Parameters
129
+
130
+ - `name` — Unique procedure name. Becomes a dynamic property on the return object.
131
+ - `config.description` — Human-readable description.
132
+ - `config.schema.params` — Input schema. Validated at runtime using AJV.
133
+ - `config.schema.returnType` — Return type schema. Documentation only.
134
+ - `handler` — Async function `(ctx, params) => Promise<result>`.
135
+
136
+ ### Handler Context
137
+
138
+ | Property | Type | Description |
139
+ |----------|------|-------------|
140
+ | `...TContext` | varies | All base context fields |
141
+ | `error(message, meta?)` | `Function` | Creates `ProcedureError` — throw this for business logic errors |
142
+ | `signal?` | `AbortSignal` | Present when HTTP implementation injects it (Express/Hono do this automatically) |
143
+ | `isPrevalidated?` | `boolean` | `true` when HTTP impl already validated params — skips AJV validation |
144
+
145
+ ### Return Value
146
+
147
+ - `{ [name]: handler }` — Dynamically named property (e.g., `{ GetUser: fn }`)
148
+ - `{ procedure: handler }` — Same handler under a fixed key
149
+ - `{ info: metadata }` — Registration metadata with computed JSON schemas and validation functions
150
+
151
+ ### schema.input (Structured Multi-Channel Input)
152
+
153
+ `schema.input` is an alternative to `schema.params` for multi-channel input. Mutually exclusive with `schema.params`.
154
+
155
+ ```typescript
156
+ Create('UpdateUser', {
157
+ path: '/users/:id',
158
+ method: 'put',
159
+ schema: {
160
+ input: {
161
+ pathParams: Type.Object({ id: Type.String() }),
162
+ body: Type.Object({ name: Type.String() }),
163
+ },
164
+ returnType: Type.Object({ ok: Type.Boolean() }),
165
+ },
166
+ }, async (ctx, { pathParams, body }) => {
167
+ // Each channel independently typed via TInput generic
168
+ return { ok: true }
169
+ })
170
+ ```
171
+
172
+ - Each key in `input` is independently validated with per-channel error messages
173
+ - `ProcedureRegistrationError` thrown at registration if both `params` and `input` defined
174
+ - `TInput` generic inferred from `schema.input` — handler params type computed as `{ [K in keyof TInput]: TSchemaLib<TInput[K]> }`
175
+
176
+ ### Throws
177
+
178
+ - `ProcedureRegistrationError` — If schema extraction/compilation fails, or duplicate name
179
+ - `ProcedureValidationError` — At call time if params fail validation
180
+ - `ProcedureError` — At call time if handler throws (wraps original error in `cause`)
181
+
182
+ ---
183
+
184
+ ## CreateStream\<TName, TParams, TYieldType, TReturnType\>(name, config, handler)
185
+
186
+ Registers a streaming procedure (async generator).
187
+
188
+ ```typescript
189
+ function CreateStream<TName extends string, TParams, TYieldType, TReturnType = void>(
190
+ name: TName,
191
+ config: {
192
+ description?: string
193
+ schema?: {
194
+ params?: TParams // Validated at runtime
195
+ yieldType?: TYieldType // Validated per-yield only if validateYields: true
196
+ returnType?: TReturnType // Documentation only
197
+ }
198
+ validateYields?: boolean // Default: false
199
+ } & TExtendedConfig,
200
+ handler: (
201
+ ctx: TContext & TStreamContext & { isPrevalidated?: boolean },
202
+ params: TSchemaLib<TParams>
203
+ ) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
204
+ ): {
205
+ [K in TName]: (ctx: TContext, params: TSchemaLib<TParams>) => AsyncGenerator<...>
206
+ procedure: (ctx: TContext, params: TSchemaLib<TParams>) => AsyncGenerator<...>
207
+ info: {
208
+ name: TName
209
+ isStream: true
210
+ description?: string
211
+ schema: { params?: TJSONSchema; yieldType?: TJSONSchema; returnType?: TJSONSchema }
212
+ validation?: {
213
+ params?: (params: any) => { errors?: TSchemaValidationError[] }
214
+ yield?: (value: any) => { errors?: TSchemaValidationError[] }
215
+ }
216
+ } & TExtendedConfig
217
+ }
218
+ ```
219
+
220
+ ### Handler Context
221
+
222
+ Same as Create, except:
223
+
224
+ - `ctx.signal` — **Always present** (guaranteed `AbortSignal`)
225
+ - On normal stream completion: `signal.reason === 'stream-completed'`
226
+ - On client disconnect: `signal.aborted === true`, `signal.reason` is the abort reason
227
+
228
+ ### Signal Lifecycle
229
+
230
+ ```
231
+ Stream starts → signal.aborted = false
232
+ Client aborts → signal.aborted = true, reason = external reason
233
+ Stream completes → signal.aborted = true, reason = 'stream-completed'
234
+ ```
235
+
236
+ Handlers can distinguish normal completion from client disconnection by checking `signal.reason`.
237
+
238
+ ### Yield Validation
239
+
240
+ When `validateYields: true`:
241
+ - Each yielded value is validated against `schema.yieldType`
242
+ - Failures throw `ProcedureYieldValidationError`
243
+ - Default is `false` (no yield validation)
244
+
245
+ ---
246
+
247
+ ## Error Classes
248
+
249
+ ### ProcedureError
250
+
251
+ Base error class for all procedure errors.
252
+
253
+ ```typescript
254
+ class ProcedureError extends Error {
255
+ readonly procedureName: string
256
+ readonly message: string
257
+ readonly meta?: object
258
+ readonly cause?: unknown
259
+ readonly definedAt?: DefinitionLocation
260
+ readonly definitionStack?: string
261
+
262
+ constructor(procedureName: string, message: string, meta?: object, definitionInfo?: DefinitionInfo)
263
+ getDefinitionLocation(): string | undefined
264
+ }
265
+ ```
266
+
267
+ - Created via `ctx.error(message, meta?)` in handlers
268
+ - Unhandled handler exceptions are wrapped: original error in `cause`
269
+ - Stack trace enhanced with procedure definition location
270
+
271
+ ### ProcedureValidationError extends ProcedureError
272
+
273
+ Thrown when params schema validation fails.
274
+
275
+ ```typescript
276
+ class ProcedureValidationError extends ProcedureError {
277
+ readonly errors?: TSchemaValidationError[]
278
+ constructor(procedureName: string, message: string, errors?: TSchemaValidationError[], definitionInfo?: DefinitionInfo)
279
+ }
280
+ ```
281
+
282
+ - `errors` — Array of AJV error objects with `keyword`, `instancePath`, `message`, `params`
283
+ - Message format: `"Validation error message - field.path validation message, ..."`
284
+
285
+ ### ProcedureYieldValidationError extends ProcedureError
286
+
287
+ Thrown when yield validation fails in CreateStream (when `validateYields: true`).
288
+
289
+ ```typescript
290
+ class ProcedureYieldValidationError extends ProcedureError {
291
+ readonly errors?: TSchemaValidationError[]
292
+ constructor(procedureName: string, message: string, errors?: TSchemaValidationError[], definitionInfo?: DefinitionInfo)
293
+ }
294
+ ```
295
+
296
+ ### ProcedureRegistrationError extends ProcedureError
297
+
298
+ Thrown at registration time (schema extraction/compilation failure, duplicate name, or `schema.params` and `schema.input` both defined).
299
+
300
+ ```typescript
301
+ class ProcedureRegistrationError extends ProcedureError {
302
+ constructor(procedureName: string, message: string, definitionInfo?: DefinitionInfo)
303
+ }
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Schema Utilities
309
+
310
+ ### extractJsonSchema(libSchema)
311
+
312
+ Converts a TypeBox schema to JSON Schema.
313
+
314
+ ```typescript
315
+ function extractJsonSchema(libSchema: unknown): TJSONSchema | undefined
316
+ ```
317
+
318
+ - TypeBox schemas are valid JSON Schema — returned as-is
319
+ - Returns `undefined` if schema type not recognized
320
+
321
+ ### schemaParser(schema, onParseError)
322
+
323
+ Compiles schemas into AJV validator functions.
324
+
325
+ ```typescript
326
+ function schemaParser(
327
+ schema: { params?: unknown; returnType?: unknown; yieldType?: unknown; input?: Record<string, unknown> },
328
+ onParseError: (errors: Record<string, string>) => void
329
+ ): TSchemaParsed
330
+ ```
331
+
332
+ ### isTypeboxSchema(schema)
333
+
334
+ Type guard for TypeBox schema detection.
335
+
336
+ ```typescript
337
+ function isTypeboxSchema(schema: any): schema is TSchema
338
+ ```
339
+
340
+ ### TSchemaLib\<SchemaLibType\>
341
+
342
+ Maps schema type to inferred TypeScript type:
343
+ - TypeBox `TSchema` → `Static<T>`
344
+ - Otherwise → `unknown`
345
+
346
+ ---
347
+
348
+ ## ExpressRPCAppBuilder
349
+
350
+ HTTP implementation for Express.
351
+
352
+ ```typescript
353
+ class ExpressRPCAppBuilder {
354
+ constructor(config?: {
355
+ app?: express.Express
356
+ pathPrefix?: string
357
+ onRequestStart?: (req: express.Request) => void
358
+ onRequestEnd?: (req: express.Request, res: express.Response) => void
359
+ onSuccess?: (procedure: TProcedureRegistration, req: express.Request, res: express.Response) => void
360
+ onError?: (procedure: TProcedureRegistration, req: express.Request, res: express.Response, error: Error) => void
361
+ })
362
+
363
+ register<TFactory>(
364
+ factory: TFactory,
365
+ factoryContext: Context | ((req: Request) => Context | Promise<Context>),
366
+ extendProcedureDoc?: ({ base, procedure }) => Record<string, any>
367
+ ): this
368
+
369
+ build(): express.Application
370
+
371
+ static makeRPCHttpRoutePath(params: { name: string; config: RPCConfig; prefix?: string }): string
372
+
373
+ get app(): express.Express
374
+ get docs(): RPCHttpRouteDoc[]
375
+ }
376
+ ```
377
+
378
+ ### Route Path
379
+
380
+ `POST {pathPrefix}/{scope}/{kebab-name}/{version}`
381
+
382
+ - `scope` can be string or string array (joined as path segments)
383
+ - Procedure name is converted to kebab-case
384
+
385
+ ### Context Resolution
386
+
387
+ - Static object: `builder.register(factory, { userId: 'system' })`
388
+ - Sync function: `builder.register(factory, (req) => ({ auth: req.headers.authorization }))`
389
+ - Async function: `builder.register(factory, async (req) => ({ user: await getUser(req) }))`
390
+
391
+ ### Signal Injection
392
+
393
+ Express builder creates a lazy AbortController and aborts on request close. The `signal` is injected into handler context automatically.
394
+
395
+ ---
396
+
397
+ ## HonoRPCAppBuilder
398
+
399
+ HTTP implementation for Hono. Same API as ExpressRPCAppBuilder but receives Hono `Context` instead of Express `Request`.
400
+
401
+ ```typescript
402
+ class HonoRPCAppBuilder {
403
+ constructor(config?: {
404
+ app?: Hono
405
+ pathPrefix?: string
406
+ onRequestStart?: (c: Context) => void
407
+ onRequestEnd?: (c: Context) => void
408
+ onSuccess?: (procedure: TProcedureRegistration, c: Context) => void
409
+ onError?: (procedure: TProcedureRegistration, c: Context, error: Error) => Response | Promise<Response>
410
+ })
411
+
412
+ register<TFactory>(
413
+ factory: TFactory,
414
+ factoryContext: Context | ((c: HonoContext) => Context | Promise<Context>),
415
+ extendProcedureDoc?: ({ base, procedure }) => Record<string, any>
416
+ ): this
417
+
418
+ build(): Hono
419
+ get app(): Hono
420
+ get docs(): RPCHttpRouteDoc[]
421
+ }
422
+ ```
423
+
424
+ ### Signal Injection
425
+
426
+ Uses `c.req.raw.signal` — the native Request signal from the Hono context.
427
+
428
+ ---
429
+
430
+ ## HonoStreamAppBuilder\<TErrorData\>
431
+
432
+ Streaming HTTP implementation for Hono. Supports SSE and text streaming modes.
433
+
434
+ ```typescript
435
+ class HonoStreamAppBuilder<TErrorData = unknown> {
436
+ constructor(config?: {
437
+ app?: Hono
438
+ pathPrefix?: string
439
+ defaultStreamMode?: StreamMode // 'sse' | 'text', default 'sse'
440
+ onRequestStart?: (c: Context) => void
441
+ onRequestEnd?: (c: Context) => void
442
+ onStreamStart?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
443
+ onStreamEnd?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
444
+ onPreStreamError?: (procedure: TStreamProcedureRegistration, c: Context, error: Error) => Response | Promise<Response>
445
+ onMidStreamError?: (procedure: TStreamProcedureRegistration, c: Context, error: Error) => MidStreamErrorResult<TErrorData> | undefined
446
+ })
447
+
448
+ register<TFactory>(
449
+ factory: TFactory,
450
+ factoryContext: Context | ((c: HonoContext) => Context | Promise<Context>),
451
+ options?: {
452
+ streamMode?: StreamMode
453
+ extendProcedureDoc?: ({ base, procedure }) => Record<string, any>
454
+ }
455
+ ): this
456
+
457
+ build(): Hono
458
+ get app(): Hono
459
+ get docs(): StreamHttpRouteDoc[]
460
+ }
461
+ ```
462
+
463
+ ### Stream Modes
464
+
465
+ **SSE mode** (`'sse'`):
466
+ - Content-Type: `text/event-stream`
467
+ - Each yield → `event: {name}\nid: {counter}\ndata: {json}\n\n`
468
+ - Supports custom event/id/retry via `sse()` helper
469
+
470
+ **Text mode** (`'text'`):
471
+ - Content-Type: `text/plain`
472
+ - Each yield → `{json}\n`
473
+
474
+ ### sse(data, options?)
475
+
476
+ Attaches SSE metadata to a yielded value.
477
+
478
+ ```typescript
479
+ function sse<T extends object>(data: T, options?: {
480
+ event?: string // SSE event type (default: procedure name)
481
+ id?: string // SSE event ID (default: sequential counter)
482
+ retry?: number // Reconnection interval in ms
483
+ }): T
484
+ ```
485
+
486
+ ### HTTP Methods
487
+
488
+ Both GET and POST are registered for each streaming procedure:
489
+ - **GET**: params extracted from query string
490
+ - **POST**: params extracted from JSON body
491
+
492
+ ### MidStreamErrorResult
493
+
494
+ ```typescript
495
+ type MidStreamErrorResult<TErrorData = unknown> = {
496
+ data: TErrorData // Data to yield as final event
497
+ closeStream?: boolean // Close stream after? (default: true)
498
+ }
499
+ ```
500
+
501
+ ### Error Handling
502
+
503
+ - **Pre-stream errors** (validation, context): Returns JSON error response (no streaming started). Custom handling via `onPreStreamError`.
504
+ - **Mid-stream errors** (generator throws): Yields error as final event, closes stream. Custom handling via `onMidStreamError`.
505
+
506
+ ---
507
+
508
+ ## HonoAPIAppBuilder
509
+
510
+ REST-style HTTP implementation for Hono. Routes by HTTP method with `schema.input` per-channel validation.
511
+
512
+ ```typescript
513
+ class HonoAPIAppBuilder {
514
+ constructor(config?: {
515
+ app?: Hono
516
+ pathPrefix?: string
517
+ queryParser?: (queryString: string) => Record<string, unknown>
518
+ onRequestStart?: (c: Context) => void
519
+ onRequestEnd?: (c: Context) => void
520
+ onSuccess?: (procedure: TProcedureRegistration, c: Context) => void
521
+ onError?: (procedure: TProcedureRegistration, c: Context, error: Error) => Response | Promise<Response>
522
+ })
523
+
524
+ register<TFactory>(
525
+ factory: TFactory,
526
+ factoryContext: Context | ((c: HonoContext) => Context | Promise<Context>),
527
+ extendProcedureDoc?: ({ base, procedure }) => Record<string, any>
528
+ ): this
529
+
530
+ async build(): Promise<Hono>
531
+ get app(): Hono
532
+ get docs(): APIHttpRouteDoc[]
533
+ }
534
+ ```
535
+
536
+ ### Route Path
537
+
538
+ Developer-defined via `APIConfig.path` — no auto-generation. Supports Hono path params (`:id`).
539
+
540
+ `{pathPrefix}{path}` — e.g., `/api/users/:id`
541
+
542
+ ### Input Channel Extraction
543
+
544
+ | Channel | HTTP Source | Used For |
545
+ |---------|-----------|----------|
546
+ | `pathParams` | URL path parameters (`c.req.param()`) | `/users/:id` → `{ id: '...' }` |
547
+ | `query` | Query string (parsed via `qs` or native) | `?page=2` → `{ page: 2 }` |
548
+ | `body` | JSON request body (POST/PUT/PATCH only) | `{ name: 'John' }` |
549
+ | `headers` | Request headers (AJV strips non-declared) | `{ 'x-api-key': '...' }` |
550
+
551
+ ### Default Success Status
552
+
553
+ | Method | Default Status |
554
+ |--------|---------------|
555
+ | POST | 201 |
556
+ | DELETE | 204 (no body) |
557
+ | Others | 200 |
558
+
559
+ Override with `APIConfig.successStatus`.
560
+
561
+ ### Build-Time Validation
562
+
563
+ - Path param names in path template must match `schema.input.pathParams` property names
564
+ - `schema.input.pathParams` defined but path has no `:param` segments → error
565
+ - Path has `:param` but `schema.input.pathParams` missing → error
566
+ - Property name mismatch → error with clear message
567
+
568
+ ### Query Parsing
569
+
570
+ Priority: custom `queryParser` > `qs` (optional peer dep, lazy-loaded) > native `URLSearchParams`.
571
+
572
+ Query parser resolved once at `build()` time — handlers use it synchronously.
573
+
574
+ ### Signal Injection
575
+
576
+ Uses `c.req.raw.signal` — native Request signal from Hono context.
577
+
578
+ ---
579
+
580
+ ## HTTP Types
581
+
582
+ ### RPCConfig
583
+
584
+ ```typescript
585
+ interface RPCConfig {
586
+ scope: string | string[]
587
+ version: number
588
+ }
589
+ ```
590
+
591
+ ### RPCHttpRouteDoc
592
+
593
+ ```typescript
594
+ interface RPCHttpRouteDoc extends RPCConfig {
595
+ name: string
596
+ path: string
597
+ method: 'post'
598
+ jsonSchema: {
599
+ body?: Record<string, unknown>
600
+ response?: Record<string, unknown>
601
+ }
602
+ }
603
+ ```
604
+
605
+ ### StreamHttpRouteDoc
606
+
607
+ ```typescript
608
+ interface StreamHttpRouteDoc extends RPCConfig {
609
+ name: string
610
+ path: string
611
+ methods: ('get' | 'post')[]
612
+ streamMode: StreamMode
613
+ jsonSchema: {
614
+ params?: Record<string, unknown>
615
+ yieldType?: Record<string, unknown>
616
+ returnType?: Record<string, unknown>
617
+ }
618
+ }
619
+ ```
620
+
621
+ ### APIConfig
622
+
623
+ ```typescript
624
+ interface APIConfig {
625
+ path: string
626
+ method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
627
+ successStatus?: number
628
+ }
629
+ ```
630
+
631
+ ### APIHttpRouteDoc
632
+
633
+ ```typescript
634
+ interface APIHttpRouteDoc extends APIConfig {
635
+ name: string
636
+ fullPath: string
637
+ jsonSchema: {
638
+ pathParams?: Record<string, unknown>
639
+ query?: Record<string, unknown>
640
+ body?: Record<string, unknown>
641
+ headers?: Record<string, unknown>
642
+ response?: Record<string, unknown>
643
+ }
644
+ }
645
+ ```
646
+
647
+ ### APIInput
648
+
649
+ Channel constraint helper type. Use with `satisfies` for compile-time validation of channel names.
650
+
651
+ ```typescript
652
+ type APIInput<T extends {
653
+ pathParams?: unknown
654
+ query?: unknown
655
+ body?: unknown
656
+ headers?: unknown
657
+ }> = T
658
+ ```
659
+
660
+ ### HttpMethod
661
+
662
+ ```typescript
663
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
664
+ ```
665
+
666
+ ---
667
+
668
+ ## Stack Utilities
669
+
670
+ ### captureDefinitionInfo()
671
+
672
+ Captures stack trace and extracts first user-code frame location. Used internally to enhance error messages.
673
+
674
+ ```typescript
675
+ function captureDefinitionInfo(): DefinitionInfo
676
+ ```
677
+
678
+ ### DefinitionLocation
679
+
680
+ ```typescript
681
+ type DefinitionLocation = {
682
+ file: string
683
+ line: number
684
+ column: number
685
+ raw: string
686
+ }
687
+ ```
688
+
689
+ ### DefinitionInfo
690
+
691
+ ```typescript
692
+ type DefinitionInfo = {
693
+ definedAt?: DefinitionLocation
694
+ definitionStack?: string
695
+ }
696
+ ```