ts-procedures 10.1.1 → 10.2.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/CHANGELOG.md +26 -65
- package/README.md +2 -0
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +8 -0
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +25 -22
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +24 -13
- package/agent_config/copilot/copilot-instructions.md +1 -1
- package/agent_config/cursor/cursorrules +1 -1
- package/build/adapters/hono/envelope-parity.test.js +6 -17
- package/build/adapters/hono/envelope-parity.test.js.map +1 -1
- package/build/adapters/hono/handlers/http-stream.d.ts +2 -1
- package/build/adapters/hono/handlers/http-stream.js +9 -2
- package/build/adapters/hono/handlers/http-stream.js.map +1 -1
- package/build/adapters/hono/handlers/http.d.ts +2 -1
- package/build/adapters/hono/handlers/http.js +9 -2
- package/build/adapters/hono/handlers/http.js.map +1 -1
- package/build/adapters/hono/handlers/rpc.d.ts +2 -1
- package/build/adapters/hono/handlers/rpc.js +9 -2
- package/build/adapters/hono/handlers/rpc.js.map +1 -1
- package/build/adapters/hono/handlers/stream.d.ts +2 -1
- package/build/adapters/hono/handlers/stream.js +9 -2
- package/build/adapters/hono/handlers/stream.js.map +1 -1
- package/build/adapters/hono/index.d.ts +14 -7
- package/build/adapters/hono/index.js +45 -5
- package/build/adapters/hono/index.js.map +1 -1
- package/build/adapters/hono/index.test.js +55 -0
- package/build/adapters/hono/index.test.js.map +1 -1
- package/build/adapters/hono/request.d.ts +7 -0
- package/build/adapters/hono/request.js +15 -0
- package/build/adapters/hono/request.js.map +1 -1
- package/build/adapters/hono/types.d.ts +31 -0
- package/build/adapters/hono/types.js.map +1 -1
- package/build/codegen/bin/cli.js +1 -1
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/constants.js +2 -2
- package/build/codegen/constants.js.map +1 -1
- package/build/codegen/emit/api-route.js +1 -1
- package/build/codegen/emit/api-route.js.map +1 -1
- package/build/codegen/emit/scope-file.js +1 -1
- package/build/codegen/emit/scope-file.js.map +1 -1
- package/build/codegen/emit-errors.integration.test.js +2 -2
- package/build/codegen/emit-errors.integration.test.js.map +1 -1
- package/build/codegen/resolve-envelope.js +1 -1
- package/build/codegen/resolve-envelope.js.map +1 -1
- package/build/codegen/test-helpers/run-tsc.js +1 -1
- package/build/codegen/test-helpers/run-tsc.js.map +1 -1
- package/build/core/create-http-stream.d.ts +3 -14
- package/build/core/create-http-stream.js +4 -2
- package/build/core/create-http-stream.js.map +1 -1
- package/build/core/create-http.d.ts +2 -9
- package/build/core/create-http.js +3 -2
- package/build/core/create-http.js.map +1 -1
- package/build/core/create-http.test-d.d.ts +1 -0
- package/build/core/create-http.test-d.js +104 -0
- package/build/core/create-http.test-d.js.map +1 -0
- package/build/core/create-stream.js +6 -2
- package/build/core/create-stream.js.map +1 -1
- package/build/core/create.js +5 -4
- package/build/core/create.js.map +1 -1
- package/build/core/create.test.js +63 -0
- package/build/core/create.test.js.map +1 -1
- package/build/core/internal.d.ts +16 -0
- package/build/core/internal.js +22 -0
- package/build/core/internal.js.map +1 -1
- package/build/core/procedures.d.ts +9 -16
- package/build/core/procedures.js +4 -0
- package/build/core/procedures.js.map +1 -1
- package/build/core/types.d.ts +85 -4
- package/build/errors.d.ts +33 -0
- package/build/errors.js +91 -0
- package/build/errors.js.map +1 -0
- package/build/errors.test.d.ts +1 -0
- package/build/errors.test.js +122 -0
- package/build/errors.test.js.map +1 -0
- package/build/exports.d.ts +1 -2
- package/build/exports.js +0 -4
- package/build/exports.js.map +1 -1
- package/build/implementations/http/doc-registry.d.ts +28 -0
- package/build/implementations/http/doc-registry.js +96 -0
- package/build/implementations/http/doc-registry.js.map +1 -0
- package/build/implementations/http/doc-registry.test.d.ts +1 -0
- package/build/implementations/http/doc-registry.test.js +516 -0
- package/build/implementations/http/doc-registry.test.js.map +1 -0
- package/build/implementations/http/error-taxonomy.d.ts +249 -0
- package/build/implementations/http/error-taxonomy.js +252 -0
- package/build/implementations/http/error-taxonomy.js.map +1 -0
- package/build/implementations/http/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/error-taxonomy.test.js +399 -0
- package/build/implementations/http/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/express-rpc/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/express-rpc/error-taxonomy.test.js +83 -0
- package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/express-rpc/index.d.ts +125 -0
- package/build/implementations/http/express-rpc/index.js +216 -0
- package/build/implementations/http/express-rpc/index.js.map +1 -0
- package/build/implementations/http/express-rpc/index.test.d.ts +1 -0
- package/build/implementations/http/express-rpc/index.test.js +684 -0
- package/build/implementations/http/express-rpc/index.test.js.map +1 -0
- package/build/implementations/http/express-rpc/types.d.ts +11 -0
- package/build/implementations/http/express-rpc/types.js +2 -0
- package/build/implementations/http/express-rpc/types.js.map +1 -0
- package/build/implementations/http/hono-api/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/hono-api/error-taxonomy.test.js +137 -0
- package/build/implementations/http/hono-api/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/hono-api/index.d.ts +151 -0
- package/build/implementations/http/hono-api/index.js +344 -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 +992 -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/http/hono-rpc/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js +64 -0
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/hono-rpc/index.d.ts +130 -0
- package/build/implementations/http/hono-rpc/index.js +209 -0
- package/build/implementations/http/hono-rpc/index.js.map +1 -0
- package/build/implementations/http/hono-rpc/index.test.d.ts +1 -0
- package/build/implementations/http/hono-rpc/index.test.js +828 -0
- package/build/implementations/http/hono-rpc/index.test.js.map +1 -0
- package/build/implementations/http/hono-rpc/types.d.ts +11 -0
- package/build/implementations/http/hono-rpc/types.js +2 -0
- package/build/implementations/http/hono-rpc/types.js.map +1 -0
- package/build/implementations/http/hono-stream/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/hono-stream/error-taxonomy.test.js +159 -0
- package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/hono-stream/index.d.ts +171 -0
- package/build/implementations/http/hono-stream/index.js +415 -0
- package/build/implementations/http/hono-stream/index.js.map +1 -0
- package/build/implementations/http/hono-stream/index.test.d.ts +1 -0
- package/build/implementations/http/hono-stream/index.test.js +1383 -0
- package/build/implementations/http/hono-stream/index.test.js.map +1 -0
- package/build/implementations/http/hono-stream/types.d.ts +15 -0
- package/build/implementations/http/hono-stream/types.js +2 -0
- package/build/implementations/http/hono-stream/types.js.map +1 -0
- package/build/implementations/http/on-request-error.test.d.ts +1 -0
- package/build/implementations/http/on-request-error.test.js +173 -0
- package/build/implementations/http/on-request-error.test.js.map +1 -0
- package/build/implementations/http/route-errors.test.d.ts +1 -0
- package/build/implementations/http/route-errors.test.js +139 -0
- package/build/implementations/http/route-errors.test.js.map +1 -0
- package/build/implementations/types.d.ts +192 -0
- package/build/implementations/types.js +2 -0
- package/build/implementations/types.js.map +1 -0
- package/build/index.d.ts +165 -0
- package/build/index.js +258 -0
- package/build/index.js.map +1 -0
- package/build/index.test.d.ts +1 -0
- package/build/index.test.js +920 -0
- package/build/index.test.js.map +1 -0
- package/build/schema/extract-json-schema.d.ts +2 -0
- package/build/schema/extract-json-schema.js +12 -0
- package/build/schema/extract-json-schema.js.map +1 -0
- package/build/schema/extract-json-schema.test.d.ts +1 -0
- package/build/schema/extract-json-schema.test.js +23 -0
- package/build/schema/extract-json-schema.test.js.map +1 -0
- package/build/schema/parser.d.ts +28 -0
- package/build/schema/parser.js +170 -0
- package/build/schema/parser.js.map +1 -0
- package/build/schema/parser.test.d.ts +1 -0
- package/build/schema/parser.test.js +120 -0
- package/build/schema/parser.test.js.map +1 -0
- package/build/schema/resolve-schema-lib.d.ts +12 -0
- package/build/schema/resolve-schema-lib.js +11 -0
- package/build/schema/resolve-schema-lib.js.map +1 -0
- package/build/schema/resolve-schema-lib.test.d.ts +1 -0
- package/build/schema/resolve-schema-lib.test.js +17 -0
- package/build/schema/resolve-schema-lib.test.js.map +1 -0
- package/build/schema/typebox.d.ts +1 -1
- package/build/schema/typebox.js +2 -20
- package/build/schema/typebox.js.map +1 -1
- package/build/schema/typebox.test.js +0 -10
- package/build/schema/typebox.test.js.map +1 -1
- package/build/schema/types.d.ts +8 -0
- package/build/schema/types.js +2 -0
- package/build/schema/types.js.map +1 -0
- package/build/server/context.d.ts +12 -2
- package/build/server/context.js +49 -4
- package/build/server/context.js.map +1 -1
- package/build/server/context.test.js +32 -1
- package/build/server/context.test.js.map +1 -1
- package/build/server/doc-registry.js +0 -3
- package/build/server/doc-registry.js.map +1 -1
- package/build/server/doc-registry.test.js +0 -31
- package/build/server/doc-registry.test.js.map +1 -1
- package/build/server/index.d.ts +0 -1
- package/build/server/index.js +0 -1
- package/build/server/index.js.map +1 -1
- package/build/server/types.d.ts +6 -27
- package/build/stack-utils.d.ts +25 -0
- package/build/stack-utils.js +95 -0
- package/build/stack-utils.js.map +1 -0
- package/build/stack-utils.test.d.ts +1 -0
- package/build/stack-utils.test.js +80 -0
- package/build/stack-utils.test.js.map +1 -0
- package/docs/core.md +8 -0
- package/docs/http-integrations.md +41 -27
- package/package.json +8 -8
- package/src/adapters/astro/README.md +2 -0
- package/src/adapters/hono/envelope-parity.test.ts +6 -18
- package/src/adapters/hono/handlers/http-stream.ts +12 -3
- package/src/adapters/hono/handlers/http.ts +12 -3
- package/src/adapters/hono/handlers/rpc.ts +12 -3
- package/src/adapters/hono/handlers/stream.ts +12 -3
- package/src/adapters/hono/index.test.ts +70 -0
- package/src/adapters/hono/index.ts +53 -11
- package/src/adapters/hono/request.ts +18 -0
- package/src/adapters/hono/types.ts +37 -0
- package/src/codegen/bin/cli.ts +1 -1
- package/src/codegen/constants.ts +2 -2
- package/src/codegen/emit/api-route.ts +1 -1
- package/src/codegen/emit/scope-file.ts +1 -2
- package/src/codegen/emit-errors.integration.test.ts +2 -2
- package/src/codegen/resolve-envelope.ts +1 -1
- package/src/codegen/test-helpers/run-tsc.ts +0 -1
- package/src/core/create-http-stream.ts +20 -13
- package/src/core/create-http.test-d.ts +154 -0
- package/src/core/create-http.ts +19 -8
- package/src/core/create-stream.ts +12 -4
- package/src/core/create.test.ts +74 -0
- package/src/core/create.ts +10 -5
- package/src/core/internal.ts +35 -0
- package/src/core/procedures.ts +4 -0
- package/src/core/types.ts +99 -4
- package/src/exports.ts +6 -4
- package/src/schema/typebox.test.ts +0 -12
- package/src/schema/typebox.ts +2 -21
- package/src/server/context.test.ts +39 -1
- package/src/server/context.ts +54 -4
- package/src/server/doc-registry.test.ts +0 -34
- package/src/server/doc-registry.ts +0 -3
- package/src/server/index.ts +0 -1
- package/src/server/types.ts +8 -27
- package/build/server/spec-version.d.ts +0 -24
- package/build/server/spec-version.js +0 -26
- package/build/server/spec-version.js.map +0 -1
- package/build/version.d.ts +0 -9
- package/build/version.js +0 -11
- package/build/version.js.map +0 -1
- package/docs/doc-envelope-spec-v1.md +0 -296
- package/docs/doc-envelope-v1.schema.json +0 -253
- package/src/server/spec-version.ts +0 -27
- package/src/version.ts +0 -11
|
@@ -21,10 +21,11 @@ import { installRpcRoute } from './handlers/rpc.js'
|
|
|
21
21
|
import { installRpcStreamRoute } from './handlers/stream.js'
|
|
22
22
|
import { installHttpRoute } from './handlers/http.js'
|
|
23
23
|
import { installHttpStreamRoute } from './handlers/http-stream.js'
|
|
24
|
+
import { runMiddlewareList } from './request.js'
|
|
24
25
|
import type { Context } from 'hono'
|
|
25
|
-
import type { HonoAppBuilderConfig, HonoFactoryItem, ExtendProcedureDoc } from './types.js'
|
|
26
|
+
import type { HonoAppBuilderConfig, HonoFactoryItem, ExtendProcedureDoc, HonoRequestMiddleware, RegisterOptions } from './types.js'
|
|
26
27
|
|
|
27
|
-
export type { HonoAppBuilderConfig, ExtendProcedureDoc, QueryParser } from './types.js'
|
|
28
|
+
export type { HonoAppBuilderConfig, ExtendProcedureDoc, QueryParser, HonoRequestMiddleware, RegisterOptions } from './types.js'
|
|
28
29
|
export type { OnRequestErrorContext } from './types.js'
|
|
29
30
|
export { sse } from '../../server/sse.js'
|
|
30
31
|
export type { SSEOptions } from '../../server/sse.js'
|
|
@@ -46,6 +47,7 @@ export { DocRegistry } from '../../server/doc-registry.js'
|
|
|
46
47
|
export class HonoAppBuilder<TStreamErrorData = unknown> {
|
|
47
48
|
private readonly _app: Hono
|
|
48
49
|
private readonly factories: HonoFactoryItem<any>[] = []
|
|
50
|
+
private readonly builderMiddleware: HonoRequestMiddleware[] = []
|
|
49
51
|
private _docs: AnyHttpRouteDoc[] | null = null
|
|
50
52
|
private _skipped: { name: string; reason: string }[] = []
|
|
51
53
|
private _built = false
|
|
@@ -53,6 +55,10 @@ export class HonoAppBuilder<TStreamErrorData = unknown> {
|
|
|
53
55
|
constructor(readonly config?: HonoAppBuilderConfig<TStreamErrorData>) {
|
|
54
56
|
this._app = config?.app ?? new Hono()
|
|
55
57
|
|
|
58
|
+
if (config?.middleware?.length) {
|
|
59
|
+
this.builderMiddleware.push(...config.middleware)
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
if (config?.onRequestStart) {
|
|
57
63
|
this._app.use('*', async (c, next) => {
|
|
58
64
|
config.onRequestStart!(c)
|
|
@@ -67,6 +73,36 @@ export class HonoAppBuilder<TStreamErrorData = unknown> {
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Register Hono middleware to run for routes on this builder.
|
|
78
|
+
* If no pattern is provided, uses the builder's pathPrefix (or '*').
|
|
79
|
+
*
|
|
80
|
+
* The middleware is installed on the underlying Hono app immediately
|
|
81
|
+
* (classic Hono behavior) and is also available for per-procedure
|
|
82
|
+
* sequencing inside handlers.
|
|
83
|
+
*/
|
|
84
|
+
use(patternOrMw?: string | HonoRequestMiddleware, maybeMw?: HonoRequestMiddleware): this {
|
|
85
|
+
let pattern: string | undefined
|
|
86
|
+
let mw: HonoRequestMiddleware
|
|
87
|
+
if (typeof patternOrMw === 'function') {
|
|
88
|
+
mw = patternOrMw
|
|
89
|
+
} else {
|
|
90
|
+
pattern = patternOrMw
|
|
91
|
+
mw = maybeMw!
|
|
92
|
+
}
|
|
93
|
+
if (!mw) return this
|
|
94
|
+
|
|
95
|
+
this.builderMiddleware.push(mw)
|
|
96
|
+
|
|
97
|
+
const effective = pattern ?? (this.config?.pathPrefix ? `${this.config.pathPrefix.replace(/\/$/, '')}/*` : '*')
|
|
98
|
+
this._app.use(effective as any, async (c, next) => {
|
|
99
|
+
const res = await mw(c, async () => { await next() })
|
|
100
|
+
if (res instanceof Response) return res
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return this
|
|
104
|
+
}
|
|
105
|
+
|
|
70
106
|
static makeRoutePath = _makeRoutePath
|
|
71
107
|
|
|
72
108
|
get app(): Hono {
|
|
@@ -89,16 +125,20 @@ export class HonoAppBuilder<TStreamErrorData = unknown> {
|
|
|
89
125
|
register<TFactory extends ProceduresFactory>(
|
|
90
126
|
factory: TFactory,
|
|
91
127
|
factoryContext: FactoryContextInit<ExtractContext<TFactory>, Context>,
|
|
92
|
-
options?:
|
|
93
|
-
streamMode?: StreamMode
|
|
94
|
-
extendProcedureDoc?: ExtendProcedureDoc
|
|
95
|
-
},
|
|
128
|
+
options?: RegisterOptions<TFactory>,
|
|
96
129
|
): this {
|
|
130
|
+
// Compose contexts if options.context provided: [base, extra]
|
|
131
|
+
const effectiveContext: FactoryContextInit<ExtractContext<TFactory>, Context> =
|
|
132
|
+
options?.context
|
|
133
|
+
? [factoryContext, options.context] as any
|
|
134
|
+
: factoryContext
|
|
135
|
+
|
|
97
136
|
this.factories.push({
|
|
98
137
|
factory,
|
|
99
|
-
factoryContext,
|
|
138
|
+
factoryContext: effectiveContext,
|
|
100
139
|
streamMode: options?.streamMode,
|
|
101
140
|
extendProcedureDoc: options?.extendProcedureDoc,
|
|
141
|
+
middleware: options?.middleware,
|
|
102
142
|
} as HonoFactoryItem<any>)
|
|
103
143
|
// Invalidate docs cache so the next read recomputes.
|
|
104
144
|
this._docs = null
|
|
@@ -154,22 +194,24 @@ export class HonoAppBuilder<TStreamErrorData = unknown> {
|
|
|
154
194
|
const skipped: { name: string; reason: string }[] = []
|
|
155
195
|
const cfg = this.config ?? {}
|
|
156
196
|
|
|
197
|
+
const builderMw = this.builderMiddleware
|
|
198
|
+
|
|
157
199
|
for (const item of this.factories) {
|
|
158
200
|
for (const procedure of item.factory.getProcedures().values() as Iterable<AnyProcedureRegistration<any, any>>) {
|
|
159
201
|
switch (procedure.kind) {
|
|
160
202
|
case 'rpc':
|
|
161
|
-
installRpcRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs })
|
|
203
|
+
installRpcRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs, builderMiddleware: builderMw })
|
|
162
204
|
break
|
|
163
205
|
case 'rpc-stream': {
|
|
164
206
|
const streamMode = item.streamMode ?? cfg.stream?.defaultStreamMode ?? 'sse'
|
|
165
|
-
installRpcStreamRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs, streamMode })
|
|
207
|
+
installRpcStreamRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs, streamMode, builderMiddleware: builderMw })
|
|
166
208
|
break
|
|
167
209
|
}
|
|
168
210
|
case 'http':
|
|
169
|
-
installHttpRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs })
|
|
211
|
+
installHttpRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs, builderMiddleware: builderMw })
|
|
170
212
|
break
|
|
171
213
|
case 'http-stream':
|
|
172
|
-
installHttpStreamRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs })
|
|
214
|
+
installHttpStreamRoute({ app: this._app, procedure: procedure as any, factoryItem: item, cfg, docs, builderMiddleware: builderMw })
|
|
173
215
|
break
|
|
174
216
|
default: {
|
|
175
217
|
const reason = `Unknown procedure kind "${(procedure as any).kind}"`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Context } from 'hono'
|
|
2
2
|
import type { RequestSource } from '../../server/request/params.js'
|
|
3
3
|
import type { PreStreamDispatchResult } from '../../server/errors/dispatch.js'
|
|
4
|
+
import type { HonoRequestMiddleware } from './types.js'
|
|
4
5
|
|
|
5
6
|
/** Adapts a Hono `Context` to the server layer's {@link RequestSource}. */
|
|
6
7
|
export function honoRequestSource(c: Context): RequestSource {
|
|
@@ -26,3 +27,20 @@ export function respondToDispatch(
|
|
|
26
27
|
if (result.type === 'response') return result.response
|
|
27
28
|
return c.json(result.body, result.statusCode as never)
|
|
28
29
|
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Run a list of middleware (builder + per-item) for the current request.
|
|
33
|
+
* Returns a Response if a middleware short-circuited (or threw — caller should treat throw as error).
|
|
34
|
+
* Otherwise returns undefined to continue to normal procedure handling.
|
|
35
|
+
*/
|
|
36
|
+
export async function runMiddlewareList(
|
|
37
|
+
c: Context,
|
|
38
|
+
middleware: HonoRequestMiddleware[] | undefined,
|
|
39
|
+
): Promise<Response | undefined> {
|
|
40
|
+
if (!middleware || middleware.length === 0) return undefined
|
|
41
|
+
for (const mw of middleware) {
|
|
42
|
+
const maybe = await mw(c, async () => { /* continue to next in list */ })
|
|
43
|
+
if (maybe instanceof Response) return maybe
|
|
44
|
+
}
|
|
45
|
+
return undefined
|
|
46
|
+
}
|
|
@@ -40,11 +40,41 @@ export type ExtendProcedureDoc = (params: {
|
|
|
40
40
|
procedure: AnyProcedureRegistration<any, any>
|
|
41
41
|
}) => Record<string, unknown>
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Middleware that can run before procedure handlers on a HonoAppBuilder.
|
|
45
|
+
* Similar to Hono middleware but integrated with ts-procedures dispatch.
|
|
46
|
+
*
|
|
47
|
+
* - Throw an error → routes through normal error dispatch (taxonomy, onError, 500).
|
|
48
|
+
* - Return a Response → sent verbatim (early exit, e.g. redirect or custom 401).
|
|
49
|
+
* - Call next() to continue to the procedure (or next middleware).
|
|
50
|
+
*
|
|
51
|
+
* Use for auth, tenancy, rate limiting, request ID injection, etc.
|
|
52
|
+
*/
|
|
53
|
+
export type HonoRequestMiddleware = (
|
|
54
|
+
c: Context,
|
|
55
|
+
next: () => Promise<void> | void,
|
|
56
|
+
) => Response | Promise<Response | void> | void
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Optional per-registration middleware and context overrides.
|
|
60
|
+
* `middleware` runs before the factoryContext for these procedures.
|
|
61
|
+
* `factoryContext` (when provided here) is composed with the register arg via array semantics.
|
|
62
|
+
*/
|
|
63
|
+
export type RegisterOptions<TFactory extends ProceduresFactory = ProceduresFactory> = {
|
|
64
|
+
streamMode?: StreamMode
|
|
65
|
+
extendProcedureDoc?: ExtendProcedureDoc
|
|
66
|
+
/** Per-slice middleware (run for routes from this factory only). */
|
|
67
|
+
middleware?: HonoRequestMiddleware[]
|
|
68
|
+
/** Additional context layer composed (left-to-right) with the main factoryContext. */
|
|
69
|
+
context?: FactoryContextInit<ExtractContext<TFactory>, Context>
|
|
70
|
+
}
|
|
71
|
+
|
|
43
72
|
export type HonoFactoryItem<TFactory extends ProceduresFactory = ProceduresFactory> = {
|
|
44
73
|
factory: TFactory
|
|
45
74
|
factoryContext: FactoryContextInit<ExtractContext<TFactory>, Context>
|
|
46
75
|
streamMode?: StreamMode
|
|
47
76
|
extendProcedureDoc?: ExtendProcedureDoc
|
|
77
|
+
middleware?: HonoRequestMiddleware[]
|
|
48
78
|
}
|
|
49
79
|
|
|
50
80
|
export type HonoAppBuilderConfig<TStreamErrorData = unknown> = {
|
|
@@ -64,6 +94,13 @@ export type HonoAppBuilderConfig<TStreamErrorData = unknown> = {
|
|
|
64
94
|
onRequestStart?: (c: Context) => void
|
|
65
95
|
onRequestEnd?: (c: Context) => void
|
|
66
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Builder-wide middleware run (via internal wrapping) for every procedure
|
|
99
|
+
* registered on this builder. Runs before per-registration middleware and
|
|
100
|
+
* before the factoryContext.
|
|
101
|
+
*/
|
|
102
|
+
middleware?: HonoRequestMiddleware[]
|
|
103
|
+
|
|
67
104
|
// Kind-specific blocks
|
|
68
105
|
rpc?: {
|
|
69
106
|
onSuccess?: (procedure: TProcedureRegistration<any, any>, c: Context) => void
|
package/src/codegen/bin/cli.ts
CHANGED
|
@@ -79,7 +79,7 @@ export async function loadConfigFile(configPath?: string): Promise<CodegenConfig
|
|
|
79
79
|
} catch (err) {
|
|
80
80
|
if (configPath !== undefined) {
|
|
81
81
|
// Explicit path — always throw
|
|
82
|
-
throw new Error(`[ts-procedures-codegen] Failed to load config from ${filePath}: ${err instanceof Error ? err.message : err}
|
|
82
|
+
throw new Error(`[ts-procedures-codegen] Failed to load config from ${filePath}: ${err instanceof Error ? err.message : err}`)
|
|
83
83
|
}
|
|
84
84
|
// Default path — silently ignore if not found
|
|
85
85
|
return undefined
|
package/src/codegen/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pkg from '../../package.json' with { type: 'json' }
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Stable, version-agnostic token embedded in every generated file (across all
|
|
@@ -7,4 +7,4 @@ import { PACKAGE_VERSION } from '../version.js'
|
|
|
7
7
|
* the package version (an orphan left by an older release must still match).
|
|
8
8
|
*/
|
|
9
9
|
export const CODEGEN_SIGNATURE = 'ts-procedures-codegen'
|
|
10
|
-
export const CODEGEN_HEADER = `// Auto-generated by ${CODEGEN_SIGNATURE} (v${
|
|
10
|
+
export const CODEGEN_HEADER = `// Auto-generated by ${CODEGEN_SIGNATURE} (v${pkg.version}) — do not edit`
|
|
@@ -62,7 +62,7 @@ export async function emitApiRoute(route: APIHttpRouteDoc, ctx: EmitRouteContext
|
|
|
62
62
|
|
|
63
63
|
const declarations: string[] = []
|
|
64
64
|
let paramsTypeName = 'void'
|
|
65
|
-
let returnTypeName
|
|
65
|
+
let returnTypeName = 'void'
|
|
66
66
|
|
|
67
67
|
// Track reserved names across all sub-namespaces. Model names are reserved
|
|
68
68
|
// so an ajsc-extracted sub-type can't silently merge with a referenced model.
|
|
@@ -132,8 +132,7 @@ export async function emitScopeFile(
|
|
|
132
132
|
} catch (err) {
|
|
133
133
|
const msg = err instanceof Error ? err.message : String(err)
|
|
134
134
|
throw new Error(
|
|
135
|
-
`[ts-procedures-codegen] Failed to emit route "${route.name}" (kind: ${kind}, scope: ${group.scopeKey}): ${msg}
|
|
136
|
-
{ cause: err }
|
|
135
|
+
`[ts-procedures-codegen] Failed to emit route "${route.name}" (kind: ${kind}, scope: ${group.scopeKey}): ${msg}`
|
|
137
136
|
)
|
|
138
137
|
}
|
|
139
138
|
|
|
@@ -91,7 +91,7 @@ describe('generated _errors.ts — runtime behavior', () => {
|
|
|
91
91
|
const err = e as { stdout?: Buffer; stderr?: Buffer }
|
|
92
92
|
const stdout = err.stdout?.toString() ?? ''
|
|
93
93
|
const stderr = err.stderr?.toString() ?? ''
|
|
94
|
-
throw new Error(`tsc failed in ${outDir}:\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}
|
|
94
|
+
throw new Error(`tsc failed in ${outDir}:\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}`)
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// Dynamic import of the compiled output.
|
|
@@ -164,7 +164,7 @@ describe('generated _errors.ts — runtime behavior', () => {
|
|
|
164
164
|
const err = e as { stdout?: Buffer; stderr?: Buffer }
|
|
165
165
|
const stdout = err.stdout?.toString() ?? ''
|
|
166
166
|
const stderr = err.stderr?.toString() ?? ''
|
|
167
|
-
throw new Error(`tsc failed in ${outDir}:\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}
|
|
167
|
+
throw new Error(`tsc failed in ${outDir}:\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}`)
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
const errorsUrl = `file://${join(outDir, 'out', '_errors.js')}`
|
|
@@ -21,7 +21,7 @@ export async function resolveEnvelope(input: ResolveInput): Promise<DocEnvelope>
|
|
|
21
21
|
response = await fetch(input.url)
|
|
22
22
|
} catch (err) {
|
|
23
23
|
const msg = err instanceof Error ? err.message : String(err)
|
|
24
|
-
throw new Error(`[ts-procedures-codegen] Failed to connect to ${input.url}: ${msg}
|
|
24
|
+
throw new Error(`[ts-procedures-codegen] Failed to connect to ${input.url}: ${msg}`)
|
|
25
25
|
}
|
|
26
26
|
if (!response.ok) {
|
|
27
27
|
throw new Error(`[ts-procedures-codegen] ${input.url} returned HTTP ${response.status} ${response.statusText}`)
|
|
@@ -7,15 +7,20 @@ import {
|
|
|
7
7
|
assertNotDuplicate,
|
|
8
8
|
combineSignals,
|
|
9
9
|
iterateWithGuards,
|
|
10
|
+
runWithMiddlewares,
|
|
10
11
|
shouldSkipValidation,
|
|
11
12
|
validateReqChannels,
|
|
12
13
|
} from './internal.js'
|
|
13
14
|
import type { FactoryRuntime } from './internal.js'
|
|
14
|
-
import type {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import type {
|
|
16
|
+
HttpMethod,
|
|
17
|
+
HttpReq,
|
|
18
|
+
ProcedureResult,
|
|
19
|
+
THttpReqInput,
|
|
20
|
+
THttpStreamSchema,
|
|
21
|
+
TStreamContext,
|
|
22
|
+
THttpStreamProcedureRegistration,
|
|
23
|
+
} from './types.js'
|
|
19
24
|
|
|
20
25
|
export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext, any>) {
|
|
21
26
|
/**
|
|
@@ -29,7 +34,7 @@ export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext,
|
|
|
29
34
|
*/
|
|
30
35
|
return function CreateHttpStream<
|
|
31
36
|
TName extends string,
|
|
32
|
-
TReq extends
|
|
37
|
+
TReq extends THttpReqInput | undefined,
|
|
33
38
|
TYieldType,
|
|
34
39
|
TReturnType = void,
|
|
35
40
|
TResHeaders = undefined,
|
|
@@ -42,12 +47,7 @@ export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext,
|
|
|
42
47
|
scope?: string
|
|
43
48
|
errors?: TErrorKey[]
|
|
44
49
|
description?: string
|
|
45
|
-
schema:
|
|
46
|
-
req?: TReq
|
|
47
|
-
yield?: TYieldType
|
|
48
|
-
returnType?: TReturnType
|
|
49
|
-
res?: TResHeaders extends undefined ? undefined : { headers: TResHeaders }
|
|
50
|
-
}
|
|
50
|
+
schema: THttpStreamSchema<TReq, TYieldType, TReturnType, TResHeaders>
|
|
51
51
|
validateYields?: boolean
|
|
52
52
|
},
|
|
53
53
|
handler: TResHeaders extends undefined
|
|
@@ -116,7 +116,14 @@ export function makeCreateHttpStream<TContext>(runtime: FactoryRuntime<TContext,
|
|
|
116
116
|
signal: combineSignals(incomingSignal, abortController.signal),
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
const
|
|
119
|
+
const fullCtx = { ...ctx, ...streamCtx }
|
|
120
|
+
// Factory middleware wraps obtaining the stream result (generator or {stream, headers}).
|
|
121
|
+
const handlerResult: any = runWithMiddlewares(
|
|
122
|
+
runtime.middlewares,
|
|
123
|
+
fullCtx,
|
|
124
|
+
req,
|
|
125
|
+
(c, r) => (handler as any)(c, r)
|
|
126
|
+
)
|
|
120
127
|
const guardOptions = {
|
|
121
128
|
name,
|
|
122
129
|
validateYields,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Type } from 'typebox'
|
|
2
|
+
import { describe, expectTypeOf, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { Procedures } from './procedures.js'
|
|
5
|
+
import type { HttpReturn } from './create-http.js'
|
|
6
|
+
import type {
|
|
7
|
+
HttpReq,
|
|
8
|
+
HttpSchemaInput,
|
|
9
|
+
TCreateHttpConfig,
|
|
10
|
+
THttpReqInput,
|
|
11
|
+
THttpResInput,
|
|
12
|
+
} from './types.js'
|
|
13
|
+
import type { APIInput } from '../server/types.js'
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Type tests for HTTP input shape autocomplete / DX contract
|
|
17
|
+
// These guard the shapes that power IDE autocomplete for schema.req / schema.res.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
describe('THttpReqInput / THttpResInput / APIInput', () => {
|
|
21
|
+
test('APIInput accepts only the four known channels', () => {
|
|
22
|
+
// Valid
|
|
23
|
+
const good: APIInput = {
|
|
24
|
+
pathParams: Type.Object({ id: Type.String() }),
|
|
25
|
+
query: Type.Object({}),
|
|
26
|
+
body: Type.Object({}),
|
|
27
|
+
headers: Type.Object({}),
|
|
28
|
+
}
|
|
29
|
+
expectTypeOf(good).toEqualTypeOf<APIInput>()
|
|
30
|
+
|
|
31
|
+
// Invalid channel should not be assignable
|
|
32
|
+
// @ts-expect-error - unknown channel name
|
|
33
|
+
const _bad: APIInput = { wrong: Type.Object({}) as any }
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('THttpResInput shape', () => {
|
|
37
|
+
const resShape: THttpResInput = {
|
|
38
|
+
body: Type.Object({ ok: Type.Boolean() }),
|
|
39
|
+
headers: Type.Object({ 'x-foo': Type.String() }),
|
|
40
|
+
}
|
|
41
|
+
expectTypeOf(resShape).toEqualTypeOf<THttpResInput>()
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('TCreateHttpConfig shapes for literals', () => {
|
|
46
|
+
test('config literal gets the channel keys from the shape (autocomplete contract)', () => {
|
|
47
|
+
const cfg: TCreateHttpConfig = {
|
|
48
|
+
path: '/x',
|
|
49
|
+
method: 'get',
|
|
50
|
+
schema: {
|
|
51
|
+
req: {
|
|
52
|
+
// These keys should be suggested by the IDE
|
|
53
|
+
pathParams: Type.Object({ id: Type.String() }),
|
|
54
|
+
query: undefined,
|
|
55
|
+
},
|
|
56
|
+
res: {
|
|
57
|
+
body: Type.Object({}),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
expectTypeOf(cfg).toEqualTypeOf<TCreateHttpConfig>()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('using satisfies APIInput on req is valid and preserves specific types', () => {
|
|
65
|
+
const req = {
|
|
66
|
+
pathParams: Type.Object({ id: Type.String() }),
|
|
67
|
+
query: Type.Object({ q: Type.Optional(Type.String()) }),
|
|
68
|
+
} satisfies APIInput
|
|
69
|
+
|
|
70
|
+
const cfg: TCreateHttpConfig<typeof req> = {
|
|
71
|
+
path: '/y',
|
|
72
|
+
method: 'get',
|
|
73
|
+
schema: { req },
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The assignment succeeded, proving the shape accepted the keys
|
|
77
|
+
// (specific inference of TReq from literal is a bonus)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
describe('handler inference via HttpReq / HttpReturn', () => {
|
|
82
|
+
const procs = Procedures()
|
|
83
|
+
|
|
84
|
+
test('full req shape produces correctly typed handler param', () => {
|
|
85
|
+
const { Get } = procs.CreateHttp(
|
|
86
|
+
'Get',
|
|
87
|
+
{
|
|
88
|
+
path: '/u/:id',
|
|
89
|
+
method: 'get',
|
|
90
|
+
schema: {
|
|
91
|
+
req: {
|
|
92
|
+
pathParams: Type.Object({ id: Type.String() }),
|
|
93
|
+
query: Type.Object({ verbose: Type.Optional(Type.Boolean()) }),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
async (_ctx, req) => {
|
|
98
|
+
expectTypeOf(req).toEqualTypeOf<{
|
|
99
|
+
pathParams: { id: string }
|
|
100
|
+
query: { verbose?: boolean }
|
|
101
|
+
}>()
|
|
102
|
+
// no res schema => void
|
|
103
|
+
return undefined
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
type Handler = typeof Get
|
|
108
|
+
expectTypeOf<Parameters<Handler>[1]>().toEqualTypeOf<{
|
|
109
|
+
pathParams: { id: string }
|
|
110
|
+
query: { verbose?: boolean }
|
|
111
|
+
}>()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('res with headers produces { body, headers } return shape', () => {
|
|
115
|
+
const { WithHeaders } = procs.CreateHttp(
|
|
116
|
+
'WithHeaders',
|
|
117
|
+
{
|
|
118
|
+
path: '/h',
|
|
119
|
+
method: 'get',
|
|
120
|
+
schema: {
|
|
121
|
+
res: {
|
|
122
|
+
body: Type.Object({ data: Type.String() }),
|
|
123
|
+
headers: Type.Object({ 'x-trace': Type.String() }),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
async () => ({ body: { data: 'hi' }, headers: { 'x-trace': 'abc' } })
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
type R = Awaited<ReturnType<typeof WithHeaders>>
|
|
131
|
+
expectTypeOf<R>().toEqualTypeOf<{ body: { data: string }; headers: { 'x-trace': string } }>()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('no schema.res yields void (204-style)', () => {
|
|
135
|
+
const { Logout } = procs.CreateHttp(
|
|
136
|
+
'Logout',
|
|
137
|
+
{ path: '/logout', method: 'post' },
|
|
138
|
+
async () => undefined
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
type R = Awaited<ReturnType<typeof Logout>>
|
|
142
|
+
expectTypeOf<R>().toEqualTypeOf<void>()
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Smoke test that HttpReq and HttpSchemaInput are exported and usable
|
|
147
|
+
test('HttpReq and HttpSchemaInput are exported and usable', () => {
|
|
148
|
+
type ExampleReq = HttpSchemaInput<THttpReqInput, { pathParams: { id: string } }>
|
|
149
|
+
expectTypeOf<ExampleReq>().toHaveProperty('pathParams')
|
|
150
|
+
|
|
151
|
+
// HttpReq resolves through Infer; we just check it doesn't error and has shape
|
|
152
|
+
type H = HttpReq<{ query: { q?: string } }>
|
|
153
|
+
expectTypeOf<H>().toHaveProperty('query')
|
|
154
|
+
})
|
package/src/core/create-http.ts
CHANGED
|
@@ -5,12 +5,21 @@ import { captureDefinitionInfo } from './definition-site.js'
|
|
|
5
5
|
import { applyPathPrefix, checkPathParamConsistency } from './http-route.js'
|
|
6
6
|
import {
|
|
7
7
|
assertNotDuplicate,
|
|
8
|
+
runWithMiddlewares,
|
|
8
9
|
shouldSkipValidation,
|
|
9
10
|
toProcedureError,
|
|
10
11
|
validateReqChannels,
|
|
11
12
|
} from './internal.js'
|
|
12
13
|
import type { FactoryRuntime } from './internal.js'
|
|
13
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
HttpReq,
|
|
16
|
+
ProcedureResult,
|
|
17
|
+
TCreateHttpConfig,
|
|
18
|
+
THttpProcedureRegistration,
|
|
19
|
+
THttpReqInput,
|
|
20
|
+
THttpResInput,
|
|
21
|
+
TLocalContext,
|
|
22
|
+
} from './types.js'
|
|
14
23
|
|
|
15
24
|
/**
|
|
16
25
|
* The handler return shape is gated by the DECLARED response schema, not by
|
|
@@ -26,10 +35,6 @@ export type HttpReturn<TRes> =
|
|
|
26
35
|
: TRes extends { body: infer B } ? Infer<B>
|
|
27
36
|
: void
|
|
28
37
|
|
|
29
|
-
type HttpReq<TReq> = TReq extends Record<string, unknown>
|
|
30
|
-
? Prettify<{ [K in keyof TReq]: Infer<TReq[K]> }>
|
|
31
|
-
: undefined
|
|
32
|
-
|
|
33
38
|
export function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>) {
|
|
34
39
|
/**
|
|
35
40
|
* Defines a REST-shaped HTTP procedure with per-channel input validation
|
|
@@ -40,8 +45,8 @@ export function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>)
|
|
|
40
45
|
*/
|
|
41
46
|
return function CreateHttp<
|
|
42
47
|
TName extends string,
|
|
43
|
-
TReq extends
|
|
44
|
-
TRes extends
|
|
48
|
+
TReq extends THttpReqInput | undefined = undefined,
|
|
49
|
+
TRes extends THttpResInput | undefined = undefined,
|
|
45
50
|
TErrorKey extends string = string,
|
|
46
51
|
>(
|
|
47
52
|
name: TName,
|
|
@@ -103,7 +108,13 @@ export function makeCreateHttp<TContext>(runtime: FactoryRuntime<TContext, any>)
|
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
const localCtx: TLocalContext = { error: errorFactory }
|
|
106
|
-
|
|
111
|
+
const fullCtx = { ...ctx, ...localCtx } as any
|
|
112
|
+
return await runWithMiddlewares(
|
|
113
|
+
runtime.middlewares,
|
|
114
|
+
fullCtx,
|
|
115
|
+
req,
|
|
116
|
+
(c, r) => handler(c, r as any)
|
|
117
|
+
)
|
|
107
118
|
} catch (error: any) {
|
|
108
119
|
throw toProcedureError(name, definitionInfo, error)
|
|
109
120
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
assertNotDuplicate,
|
|
8
8
|
combineSignals,
|
|
9
9
|
iterateWithGuards,
|
|
10
|
+
runWithMiddlewares,
|
|
10
11
|
shouldSkipValidation,
|
|
11
12
|
validateParams,
|
|
12
13
|
} from './internal.js'
|
|
@@ -89,10 +90,17 @@ export function makeCreateStream<TContext, TExtendedConfig>(runtime: FactoryRunt
|
|
|
89
90
|
|
|
90
91
|
// params is correctly typed at the public API boundary via conditional
|
|
91
92
|
// type; cast here because TS cannot narrow generics inside implementations
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
93
|
+
const fullCtx = { ...ctx, ...streamCtx } as Prettify<TContext & TStreamContext>
|
|
94
|
+
|
|
95
|
+
// Apply factory middleware around obtaining the user generator.
|
|
96
|
+
// Middlewares can short-circuit (return a different generator or throw)
|
|
97
|
+
// or augment before the stream starts.
|
|
98
|
+
const userGenerator = runWithMiddlewares(
|
|
99
|
+
runtime.middlewares,
|
|
100
|
+
fullCtx,
|
|
101
|
+
params,
|
|
102
|
+
(c, p) => handler(c as any, p as any)
|
|
103
|
+
) as AsyncGenerator<any, any, unknown>
|
|
96
104
|
|
|
97
105
|
return yield* iterateWithGuards(userGenerator[Symbol.asyncIterator](), {
|
|
98
106
|
name,
|