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.
- package/README.md +150 -0
- package/agent_config/bin/postinstall.mjs +105 -0
- package/agent_config/bin/setup.mjs +286 -0
- package/agent_config/claude-code/.claude-plugin/plugin.json +5 -0
- package/agent_config/claude-code/agents/ts-procedures-architect.md +188 -0
- package/agent_config/claude-code/skills/guide/SKILL.md +142 -0
- package/agent_config/claude-code/skills/guide/anti-patterns.md +608 -0
- package/agent_config/claude-code/skills/guide/api-reference.md +696 -0
- package/agent_config/claude-code/skills/guide/patterns.md +727 -0
- package/agent_config/claude-code/skills/review/SKILL.md +53 -0
- package/agent_config/claude-code/skills/review/checklist.md +163 -0
- package/agent_config/claude-code/skills/scaffold/SKILL.md +56 -0
- package/agent_config/claude-code/skills/scaffold/templates/express-rpc.md +134 -0
- package/agent_config/claude-code/skills/scaffold/templates/hono-api.md +169 -0
- package/agent_config/claude-code/skills/scaffold/templates/hono-rpc.md +139 -0
- package/agent_config/claude-code/skills/scaffold/templates/hono-stream.md +134 -0
- package/agent_config/claude-code/skills/scaffold/templates/procedure.md +77 -0
- package/agent_config/claude-code/skills/scaffold/templates/stream-procedure.md +113 -0
- package/agent_config/copilot/copilot-instructions.md +290 -0
- package/agent_config/cursor/cursorrules +290 -0
- package/agent_config/lib/install-claude.mjs +109 -0
- package/build/implementations/http/hono-api/index.d.ts +102 -0
- package/build/implementations/http/hono-api/index.js +339 -0
- package/build/implementations/http/hono-api/index.js.map +1 -0
- package/build/implementations/http/hono-api/index.test.d.ts +1 -0
- package/build/implementations/http/hono-api/index.test.js +983 -0
- package/build/implementations/http/hono-api/index.test.js.map +1 -0
- package/build/implementations/http/hono-api/types.d.ts +13 -0
- package/build/implementations/http/hono-api/types.js +2 -0
- package/build/implementations/http/hono-api/types.js.map +1 -0
- package/build/implementations/types.d.ts +44 -0
- package/build/index.d.ts +28 -6
- package/build/index.js +28 -0
- package/build/index.js.map +1 -1
- package/build/schema/compute-schema.d.ts +5 -0
- package/build/schema/compute-schema.js +8 -1
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/parser.d.ts +6 -5
- package/build/schema/parser.js +54 -0
- package/build/schema/parser.js.map +1 -1
- package/package.json +14 -4
- package/src/implementations/http/README.md +45 -2
- package/src/implementations/http/hono-api/index.test.ts +1328 -0
- package/src/implementations/http/hono-api/index.ts +461 -0
- package/src/implementations/http/hono-api/types.ts +16 -0
- package/src/implementations/types.ts +52 -0
- package/src/index.ts +87 -10
- package/src/schema/compute-schema.ts +23 -2
- 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
|
+
```
|