ts-procedures 7.2.0 → 8.0.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 +65 -3
- package/agent_config/claude-code/agents/ts-procedures-architect.md +6 -8
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +30 -33
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +139 -53
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +208 -231
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +80 -153
- package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures-review/checklist.md +4 -5
- package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +4 -7
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono.md +223 -0
- package/agent_config/copilot/copilot-instructions.md +36 -48
- package/agent_config/cursor/cursorrules +36 -48
- package/build/client/call.js +4 -1
- package/build/client/call.js.map +1 -1
- package/build/client/call.test.js +23 -0
- package/build/client/call.test.js.map +1 -1
- package/build/client/fetch-adapter.js +3 -1
- package/build/client/fetch-adapter.js.map +1 -1
- package/build/client/fetch-adapter.test.js +11 -1
- package/build/client/fetch-adapter.test.js.map +1 -1
- package/build/client/index.test.js +7 -7
- package/build/client/index.test.js.map +1 -1
- package/build/client/request-builder.d.ts +1 -1
- package/build/client/request-builder.js +2 -2
- package/build/client/request-builder.js.map +1 -1
- package/build/client/stream.js +13 -2
- package/build/client/stream.js.map +1 -1
- package/build/client/stream.test.js +32 -7
- package/build/client/stream.test.js.map +1 -1
- package/build/client/typed-error-dispatch.test.js +8 -92
- package/build/client/typed-error-dispatch.test.js.map +1 -1
- package/build/client/types.d.ts +21 -3
- package/build/codegen/bin/cli.js +0 -0
- package/build/codegen/e2e.test.js +87 -23
- package/build/codegen/e2e.test.js.map +1 -1
- package/build/codegen/emit-errors.integration.test.js +1 -1
- package/build/codegen/emit-errors.integration.test.js.map +1 -1
- package/build/codegen/emit-scope.js +308 -47
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +363 -110
- package/build/codegen/emit-scope.test.js.map +1 -1
- package/build/codegen/pipeline.test.js +7 -7
- package/build/codegen/pipeline.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/resolve-envelope.test.js +5 -5
- package/build/codegen/resolve-envelope.test.js.map +1 -1
- package/build/codegen/targets/_shared/route-slots.d.ts +8 -3
- package/build/codegen/targets/_shared/route-slots.js +49 -8
- package/build/codegen/targets/_shared/route-slots.js.map +1 -1
- package/build/codegen/targets/_shared/route-slots.test.js +99 -26
- package/build/codegen/targets/_shared/route-slots.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js +88 -17
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js +9 -6
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/integration.test.js +6 -0
- package/build/codegen/targets/kotlin/integration.test.js.map +1 -1
- package/build/codegen/targets/swift/access-level.test.js +8 -11
- package/build/codegen/targets/swift/access-level.test.js.map +1 -1
- package/build/codegen/targets/swift/emit-route-swift.test.js +91 -20
- package/build/codegen/targets/swift/emit-route-swift.test.js.map +1 -1
- package/build/codegen/targets/swift/emit-scope-swift.test.js +12 -9
- package/build/codegen/targets/swift/emit-scope-swift.test.js.map +1 -1
- package/build/codegen/targets/swift/integration.test.js +6 -0
- package/build/codegen/targets/swift/integration.test.js.map +1 -1
- package/build/create-http-stream.d.ts +58 -0
- package/build/create-http-stream.js +122 -0
- package/build/create-http-stream.js.map +1 -0
- package/build/create-http-stream.test.js +88 -0
- package/build/create-http-stream.test.js.map +1 -0
- package/build/create-http.d.ts +49 -0
- package/build/create-http.js +108 -0
- package/build/create-http.js.map +1 -0
- package/build/create-http.test.js +137 -0
- package/build/create-http.test.js.map +1 -0
- package/build/create-stream.d.ts +35 -0
- package/build/create-stream.js +123 -0
- package/build/create-stream.js.map +1 -0
- package/build/create-stream.test.js +428 -0
- package/build/create-stream.test.js.map +1 -0
- package/build/create.d.ts +28 -0
- package/build/create.js +82 -0
- package/build/create.js.map +1 -0
- package/build/create.test.js +483 -0
- package/build/create.test.js.map +1 -0
- package/build/exports.d.ts +2 -0
- package/build/implementations/http/astro/index.test.js +20 -12
- package/build/implementations/http/astro/index.test.js.map +1 -1
- package/build/implementations/http/doc-registry.js +1 -1
- package/build/implementations/http/doc-registry.js.map +1 -1
- package/build/implementations/http/doc-registry.test.js +36 -5
- package/build/implementations/http/doc-registry.test.js.map +1 -1
- package/build/implementations/http/error-dispatch.d.ts +76 -0
- package/build/implementations/http/error-dispatch.js +77 -0
- package/build/implementations/http/error-dispatch.js.map +1 -0
- package/build/implementations/http/error-dispatch.test.js +254 -0
- package/build/implementations/http/error-dispatch.test.js.map +1 -0
- package/build/implementations/http/error-taxonomy.d.ts +5 -5
- package/build/implementations/http/hono/docs/http-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/http-doc.js +42 -0
- package/build/implementations/http/hono/docs/http-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/http-stream-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/http-stream-doc.js +40 -0
- package/build/implementations/http/hono/docs/http-stream-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/rpc-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/rpc-doc.js +24 -0
- package/build/implementations/http/hono/docs/rpc-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/stream-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/stream-doc.js +42 -0
- package/build/implementations/http/hono/docs/stream-doc.js.map +1 -0
- package/build/implementations/http/hono/handlers/http-stream.d.ts +10 -0
- package/build/implementations/http/hono/handlers/http-stream.js +123 -0
- package/build/implementations/http/hono/handlers/http-stream.js.map +1 -0
- package/build/implementations/http/hono/handlers/http-stream.test.js +128 -0
- package/build/implementations/http/hono/handlers/http-stream.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/http.d.ts +10 -0
- package/build/implementations/http/hono/handlers/http.js +115 -0
- package/build/implementations/http/hono/handlers/http.js.map +1 -0
- package/build/implementations/http/hono/handlers/http.test.js +118 -0
- package/build/implementations/http/hono/handlers/http.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/rpc.d.ts +11 -0
- package/build/implementations/http/hono/handlers/rpc.js +32 -0
- package/build/implementations/http/hono/handlers/rpc.js.map +1 -0
- package/build/implementations/http/hono/handlers/rpc.test.js +73 -0
- package/build/implementations/http/hono/handlers/rpc.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/stream.d.ts +23 -0
- package/build/implementations/http/hono/handlers/stream.js +147 -0
- package/build/implementations/http/hono/handlers/stream.js.map +1 -0
- package/build/implementations/http/hono/handlers/stream.test.d.ts +1 -0
- package/build/implementations/http/hono/handlers/stream.test.js +177 -0
- package/build/implementations/http/hono/handlers/stream.test.js.map +1 -0
- package/build/implementations/http/hono/index.d.ts +57 -0
- package/build/implementations/http/hono/index.js +149 -0
- package/build/implementations/http/hono/index.js.map +1 -0
- package/build/implementations/http/hono/index.test.d.ts +1 -0
- package/build/implementations/http/hono/index.test.js +274 -0
- package/build/implementations/http/hono/index.test.js.map +1 -0
- package/build/implementations/http/hono/path.d.ts +17 -0
- package/build/implementations/http/hono/path.js +39 -0
- package/build/implementations/http/hono/path.js.map +1 -0
- package/build/implementations/http/hono/path.test.d.ts +1 -0
- package/build/implementations/http/hono/path.test.js +83 -0
- package/build/implementations/http/hono/path.test.js.map +1 -0
- package/build/implementations/http/hono/types.d.ts +51 -0
- package/build/implementations/http/hono/types.js.map +1 -0
- package/build/implementations/http/on-request-error.test.js +6 -96
- package/build/implementations/http/on-request-error.test.js.map +1 -1
- package/build/implementations/http/route-errors.test.js +11 -59
- package/build/implementations/http/route-errors.test.js.map +1 -1
- package/build/implementations/types.d.ts +43 -9
- package/build/index.d.ts +125 -115
- package/build/index.js +10 -222
- package/build/index.js.map +1 -1
- package/build/index.test.js +30 -822
- package/build/index.test.js.map +1 -1
- package/build/migration.test.d.ts +1 -0
- package/build/migration.test.js +34 -0
- package/build/migration.test.js.map +1 -0
- package/build/schema/compute-schema.d.ts +11 -3
- package/build/schema/compute-schema.js +13 -7
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/parser.d.ts +11 -3
- package/build/schema/parser.js +49 -9
- package/build/schema/parser.js.map +1 -1
- package/build/stack-utils.js +8 -0
- package/build/stack-utils.js.map +1 -1
- package/build/types.d.ts +142 -0
- package/build/types.js.map +1 -0
- package/docs/astro-adapter.md +5 -5
- package/docs/core.md +34 -17
- package/docs/http-integrations.md +83 -170
- package/docs/streaming.md +3 -60
- package/docs/superpowers/plans/2026-05-07-astro-adapter.md +2 -7
- package/docs/superpowers/plans/2026-05-08-create-http.md +3355 -0
- package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +3365 -0
- package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +1 -3
- package/docs/superpowers/specs/2026-05-08-create-http-design.md +409 -0
- package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +411 -0
- package/package.json +4 -22
- package/src/client/call.test.ts +26 -0
- package/src/client/call.ts +4 -1
- package/src/client/fetch-adapter.test.ts +14 -1
- package/src/client/fetch-adapter.ts +3 -1
- package/src/client/index.test.ts +7 -7
- package/src/client/request-builder.ts +2 -2
- package/src/client/stream.test.ts +39 -7
- package/src/client/stream.ts +16 -2
- package/src/client/typed-error-dispatch.test.ts +7 -97
- package/src/client/types.ts +21 -3
- package/src/codegen/__fixtures__/users-envelope.json +119 -38
- package/src/codegen/e2e.test.ts +98 -24
- package/src/codegen/emit-errors.integration.test.ts +1 -1
- package/src/codegen/emit-scope.test.ts +395 -110
- package/src/codegen/emit-scope.ts +350 -55
- package/src/codegen/pipeline.test.ts +7 -7
- package/src/codegen/resolve-envelope.test.ts +5 -5
- package/src/codegen/resolve-envelope.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.test.ts +109 -26
- package/src/codegen/targets/_shared/route-slots.ts +48 -11
- package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +73 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +100 -17
- package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +9 -6
- package/src/codegen/targets/kotlin/integration.test.ts +19 -0
- package/src/codegen/targets/swift/__fixtures__/users-golden.swift +79 -0
- package/src/codegen/targets/swift/access-level.test.ts +8 -11
- package/src/codegen/targets/swift/emit-route-swift.test.ts +103 -20
- package/src/codegen/targets/swift/emit-scope-swift.test.ts +12 -9
- package/src/codegen/targets/swift/integration.test.ts +17 -0
- package/src/create-http-stream.test.ts +97 -0
- package/src/create-http-stream.ts +191 -0
- package/src/create-http.test.ts +163 -0
- package/src/create-http.ts +211 -0
- package/src/create-stream.test.ts +565 -0
- package/src/create-stream.ts +228 -0
- package/src/create.test.ts +658 -0
- package/src/create.ts +172 -0
- package/src/exports.ts +2 -0
- package/src/implementations/http/README.md +135 -95
- package/src/implementations/http/astro/README.md +4 -5
- package/src/implementations/http/astro/index.test.ts +25 -18
- package/src/implementations/http/doc-registry.test.ts +42 -5
- package/src/implementations/http/doc-registry.ts +1 -1
- package/src/implementations/http/error-dispatch.test.ts +283 -0
- package/src/implementations/http/error-dispatch.ts +176 -0
- package/src/implementations/http/error-taxonomy.ts +5 -5
- package/src/implementations/http/hono/docs/http-doc.ts +43 -0
- package/src/implementations/http/hono/docs/http-stream-doc.ts +44 -0
- package/src/implementations/http/hono/docs/rpc-doc.ts +34 -0
- package/src/implementations/http/hono/docs/stream-doc.ts +53 -0
- package/src/implementations/http/hono/handlers/http-stream.test.ts +150 -0
- package/src/implementations/http/hono/handlers/http-stream.ts +152 -0
- package/src/implementations/http/hono/handlers/http.test.ts +130 -0
- package/src/implementations/http/hono/handlers/http.ts +147 -0
- package/src/implementations/http/hono/handlers/rpc.test.ts +81 -0
- package/src/implementations/http/hono/handlers/rpc.ts +54 -0
- package/src/implementations/http/hono/handlers/stream.test.ts +198 -0
- package/src/implementations/http/hono/handlers/stream.ts +208 -0
- package/src/implementations/http/hono/index.test.ts +329 -0
- package/src/implementations/http/hono/index.ts +204 -0
- package/src/implementations/http/hono/path.test.ts +96 -0
- package/src/implementations/http/hono/path.ts +59 -0
- package/src/implementations/http/hono/types.ts +93 -0
- package/src/implementations/http/on-request-error.test.ts +10 -116
- package/src/implementations/http/route-errors.test.ts +11 -77
- package/src/implementations/types.ts +44 -9
- package/src/index.test.ts +35 -1091
- package/src/index.ts +50 -474
- package/src/migration.test.ts +48 -0
- package/src/schema/compute-schema.ts +26 -12
- package/src/schema/parser.ts +62 -12
- package/src/stack-utils.ts +8 -0
- package/src/types.ts +133 -0
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +0 -137
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +0 -173
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +0 -142
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +0 -147
- package/build/implementations/http/express-rpc/error-taxonomy.test.js +0 -83
- package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/express-rpc/index.d.ts +0 -125
- package/build/implementations/http/express-rpc/index.js +0 -216
- package/build/implementations/http/express-rpc/index.js.map +0 -1
- package/build/implementations/http/express-rpc/index.test.js +0 -684
- package/build/implementations/http/express-rpc/index.test.js.map +0 -1
- package/build/implementations/http/express-rpc/types.d.ts +0 -11
- package/build/implementations/http/express-rpc/types.js.map +0 -1
- package/build/implementations/http/hono-api/error-taxonomy.test.js +0 -137
- package/build/implementations/http/hono-api/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-api/index.d.ts +0 -151
- package/build/implementations/http/hono-api/index.js +0 -344
- package/build/implementations/http/hono-api/index.js.map +0 -1
- package/build/implementations/http/hono-api/index.test.js +0 -992
- package/build/implementations/http/hono-api/index.test.js.map +0 -1
- package/build/implementations/http/hono-api/types.d.ts +0 -13
- package/build/implementations/http/hono-api/types.js.map +0 -1
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js +0 -64
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-rpc/index.d.ts +0 -130
- package/build/implementations/http/hono-rpc/index.js +0 -209
- package/build/implementations/http/hono-rpc/index.js.map +0 -1
- package/build/implementations/http/hono-rpc/index.test.js +0 -828
- package/build/implementations/http/hono-rpc/index.test.js.map +0 -1
- package/build/implementations/http/hono-rpc/types.d.ts +0 -11
- package/build/implementations/http/hono-rpc/types.js +0 -2
- package/build/implementations/http/hono-rpc/types.js.map +0 -1
- package/build/implementations/http/hono-stream/error-taxonomy.test.js +0 -159
- package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-stream/index.d.ts +0 -171
- package/build/implementations/http/hono-stream/index.js +0 -415
- package/build/implementations/http/hono-stream/index.js.map +0 -1
- package/build/implementations/http/hono-stream/index.test.js +0 -1383
- package/build/implementations/http/hono-stream/index.test.js.map +0 -1
- package/build/implementations/http/hono-stream/types.d.ts +0 -15
- package/build/implementations/http/hono-stream/types.js +0 -2
- package/build/implementations/http/hono-stream/types.js.map +0 -1
- package/src/implementations/http/express-rpc/README.md +0 -280
- package/src/implementations/http/express-rpc/error-taxonomy.test.ts +0 -103
- package/src/implementations/http/express-rpc/index.test.ts +0 -957
- package/src/implementations/http/express-rpc/index.ts +0 -327
- package/src/implementations/http/express-rpc/types.ts +0 -16
- package/src/implementations/http/hono-api/README.md +0 -284
- package/src/implementations/http/hono-api/error-taxonomy.test.ts +0 -179
- package/src/implementations/http/hono-api/index.test.ts +0 -1341
- package/src/implementations/http/hono-api/index.ts +0 -519
- package/src/implementations/http/hono-api/types.ts +0 -16
- package/src/implementations/http/hono-rpc/README.md +0 -357
- package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +0 -82
- package/src/implementations/http/hono-rpc/index.test.ts +0 -1107
- package/src/implementations/http/hono-rpc/index.ts +0 -320
- package/src/implementations/http/hono-rpc/types.ts +0 -16
- package/src/implementations/http/hono-stream/README.md +0 -559
- package/src/implementations/http/hono-stream/error-taxonomy.test.ts +0 -178
- package/src/implementations/http/hono-stream/index.test.ts +0 -1804
- package/src/implementations/http/hono-stream/index.ts +0 -622
- package/src/implementations/http/hono-stream/types.ts +0 -20
- /package/build/{implementations/http/express-rpc/error-taxonomy.test.d.ts → create-http-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/express-rpc/index.test.d.ts → create-http.test.d.ts} +0 -0
- /package/build/{implementations/http/hono-api/error-taxonomy.test.d.ts → create-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/hono-api/index.test.d.ts → create.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-rpc/error-taxonomy.test.d.ts → error-dispatch.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-rpc/index.test.d.ts → hono/handlers/http-stream.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-stream/error-taxonomy.test.d.ts → hono/handlers/http.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-stream/index.test.d.ts → hono/handlers/rpc.test.d.ts} +0 -0
- /package/build/implementations/http/{express-rpc → hono}/types.js +0 -0
- /package/build/{implementations/http/hono-api/types.js → types.js} +0 -0
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
import { Hono, Context } from 'hono'
|
|
2
|
-
import { streamSSE, streamText } from 'hono/streaming'
|
|
3
|
-
import { kebabCase } from 'es-toolkit/string'
|
|
4
|
-
import { castArray } from 'es-toolkit/compat'
|
|
5
|
-
import { TStreamProcedureRegistration } from '../../../index.js'
|
|
6
|
-
import { ExtractConfig, ExtractContext, ProceduresFactory, RPCConfig } from '../../types.js'
|
|
7
|
-
import { HonoStreamFactoryItem, StreamHttpRouteDoc, StreamMode } from './types.js'
|
|
8
|
-
import { ProcedureValidationError } from '../../../errors.js'
|
|
9
|
-
import {
|
|
10
|
-
ErrorTaxonomy,
|
|
11
|
-
ErrorTaxonomyEntry,
|
|
12
|
-
UnknownErrorConfig,
|
|
13
|
-
defineErrorTaxonomy,
|
|
14
|
-
resolveErrorResponse,
|
|
15
|
-
} from '../error-taxonomy.js'
|
|
16
|
-
|
|
17
|
-
export type { StreamHttpRouteDoc, StreamMode }
|
|
18
|
-
export { defineErrorTaxonomy }
|
|
19
|
-
export type { ErrorTaxonomy, ErrorTaxonomyEntry, UnknownErrorConfig }
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Context passed to the `onRequestError` observer. `raw` is the Hono
|
|
23
|
-
* `Context` for the in-flight request — cast at the use site if you need
|
|
24
|
-
* framework-specific fields.
|
|
25
|
-
*/
|
|
26
|
-
export type OnRequestErrorContext = {
|
|
27
|
-
err: unknown
|
|
28
|
-
procedure: TStreamProcedureRegistration
|
|
29
|
-
raw: Context
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type SSEOptions = {
|
|
33
|
-
event?: string
|
|
34
|
-
id?: string
|
|
35
|
-
retry?: number
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const sseMetadata = new WeakMap<object, SSEOptions>()
|
|
39
|
-
|
|
40
|
-
export function sse<T extends object>(data: T, options?: SSEOptions): T {
|
|
41
|
-
sseMetadata.set(data, options ?? {})
|
|
42
|
-
return data
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getSSEMeta(value: unknown): SSEOptions | undefined {
|
|
46
|
-
if (typeof value === 'object' && value !== null) {
|
|
47
|
-
return sseMetadata.get(value)
|
|
48
|
-
}
|
|
49
|
-
return undefined
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Result from onMidStreamError callback.
|
|
54
|
-
* @property data - The data to write as the SSE `data:` field content (should match yieldType schema)
|
|
55
|
-
* @property closeStream - Whether to close the stream after writing (defaults to true)
|
|
56
|
-
*/
|
|
57
|
-
export type MidStreamErrorResult<TErrorData = unknown> = {
|
|
58
|
-
data: TErrorData
|
|
59
|
-
closeStream?: boolean
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export type HonoStreamAppBuilderConfig<TErrorData = unknown> = {
|
|
63
|
-
/**
|
|
64
|
-
* An existing Hono application instance to use.
|
|
65
|
-
* If not provided, a new instance will be created.
|
|
66
|
-
*/
|
|
67
|
-
app?: Hono
|
|
68
|
-
/** Optional path prefix for all stream routes. */
|
|
69
|
-
pathPrefix?: string
|
|
70
|
-
/** Default stream mode for all routes. Defaults to 'sse'. */
|
|
71
|
-
defaultStreamMode?: StreamMode
|
|
72
|
-
onRequestStart?: (c: Context) => void
|
|
73
|
-
onRequestEnd?: (c: Context) => void
|
|
74
|
-
onStreamStart?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
|
|
75
|
-
onStreamEnd?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
|
|
76
|
-
/**
|
|
77
|
-
* Declarative error-to-response mapping (one of the two peer error modes).
|
|
78
|
-
* Thrown error classes map to status codes + bodies declaratively. The
|
|
79
|
-
* taxonomy applies to BOTH pre-stream errors (where the status code is
|
|
80
|
-
* honored) AND mid-stream errors (where only the body shape is honored —
|
|
81
|
-
* the HTTP status is already committed once streaming starts; the body is
|
|
82
|
-
* written as the SSE `event: 'error'` data, or a JSON line in text mode,
|
|
83
|
-
* for the client's error registry to dispatch). See hono-api for the full
|
|
84
|
-
* taxonomy contract.
|
|
85
|
-
*/
|
|
86
|
-
errors?: ErrorTaxonomy
|
|
87
|
-
/**
|
|
88
|
-
* Fallback serializer when a taxonomy is configured but no entry matches.
|
|
89
|
-
*/
|
|
90
|
-
unknownError?: UnknownErrorConfig
|
|
91
|
-
/**
|
|
92
|
-
* Imperative pre-stream error callback — the other peer error mode.
|
|
93
|
-
* Receives every pre-stream error directly and returns the HTTP response.
|
|
94
|
-
* Use this instead of `errors` when you want full control over the response
|
|
95
|
-
* shape, or alongside it for the tail of errors the taxonomy doesn't cover.
|
|
96
|
-
*
|
|
97
|
-
* Mid-stream errors always go through `onMidStreamError`.
|
|
98
|
-
*/
|
|
99
|
-
onError?: (
|
|
100
|
-
procedure: TStreamProcedureRegistration,
|
|
101
|
-
c: Context,
|
|
102
|
-
error: ProcedureValidationError | Error
|
|
103
|
-
) => Response | Promise<Response>
|
|
104
|
-
/**
|
|
105
|
-
* Cross-cutting observer — fires for every caught pre-stream error, BEFORE
|
|
106
|
-
* dispatch to the taxonomy or `onError`. Awaited. Cannot mutate the
|
|
107
|
-
* response. Intended for logging, tracing, and metrics (Sentry, Datadog,
|
|
108
|
-
* OpenTelemetry). Any error thrown inside the observer is swallowed and
|
|
109
|
-
* logged so the primary dispatch flow is never disrupted.
|
|
110
|
-
*/
|
|
111
|
-
onRequestError?: (ctx: OnRequestErrorContext) => void | Promise<void>
|
|
112
|
-
/**
|
|
113
|
-
* Called for errors DURING streaming (generator throws).
|
|
114
|
-
* Return value is written to the stream as a yield.
|
|
115
|
-
* Should return a value matching your yieldType schema (e.g., error variant of a union).
|
|
116
|
-
* Return undefined to use default behavior (writes { error: message }).
|
|
117
|
-
*
|
|
118
|
-
* Use sse() to attach SSE metadata (event, id, retry) to the error data object.
|
|
119
|
-
*
|
|
120
|
-
* @returns { data, closeStream? } - data to yield, whether to close after (default true)
|
|
121
|
-
*/
|
|
122
|
-
onMidStreamError?: (
|
|
123
|
-
procedure: TStreamProcedureRegistration,
|
|
124
|
-
c: Context,
|
|
125
|
-
error: Error
|
|
126
|
-
) => MidStreamErrorResult<TErrorData> | undefined
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Builder class for creating a Hono application with streaming RPC routes.
|
|
131
|
-
*
|
|
132
|
-
* Usage:
|
|
133
|
-
* const StreamRPC = Procedures<StreamContext, RPCConfig>()
|
|
134
|
-
*
|
|
135
|
-
* const streamApp = new HonoStreamAppBuilder()
|
|
136
|
-
* .register(StreamRPC, (c): Promise<StreamContext> => { /* context resolution logic * / })
|
|
137
|
-
* .build();
|
|
138
|
-
*
|
|
139
|
-
* const app = streamApp.app; // Hono application
|
|
140
|
-
* const docs = streamApp.docs; // Stream route documentation
|
|
141
|
-
*/
|
|
142
|
-
export class HonoStreamAppBuilder<TErrorData = unknown> {
|
|
143
|
-
/**
|
|
144
|
-
* Constructor for HonoStreamAppBuilder.
|
|
145
|
-
*/
|
|
146
|
-
constructor(readonly config?: HonoStreamAppBuilderConfig<TErrorData>) {
|
|
147
|
-
if (config?.app) {
|
|
148
|
-
this._app = config.app
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (config?.onRequestStart) {
|
|
152
|
-
this._app.use('*', async (c, next) => {
|
|
153
|
-
config.onRequestStart!(c)
|
|
154
|
-
await next()
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Generates the stream route path based on the RPC configuration.
|
|
161
|
-
*/
|
|
162
|
-
static makeStreamHttpRoutePath({
|
|
163
|
-
name,
|
|
164
|
-
config,
|
|
165
|
-
prefix,
|
|
166
|
-
}: {
|
|
167
|
-
name: string
|
|
168
|
-
prefix?: string
|
|
169
|
-
config: RPCConfig
|
|
170
|
-
}) {
|
|
171
|
-
const normalizedPrefix = prefix ? (prefix.startsWith('/') ? prefix : `/${prefix}`) : ''
|
|
172
|
-
|
|
173
|
-
return `${normalizedPrefix}/${castArray(config.scope).map(kebabCase).join('/')}/${kebabCase(name)}/${String(config.version).trim()}`
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Instance method wrapper for makeStreamHttpRoutePath that uses the builder's pathPrefix.
|
|
178
|
-
*/
|
|
179
|
-
makeStreamHttpRoutePath(name: string, config: RPCConfig): string {
|
|
180
|
-
return HonoStreamAppBuilder.makeStreamHttpRoutePath({
|
|
181
|
-
name,
|
|
182
|
-
config,
|
|
183
|
-
prefix: this.config?.pathPrefix,
|
|
184
|
-
})
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private factories: HonoStreamFactoryItem<any>[] = []
|
|
188
|
-
|
|
189
|
-
private _app: Hono = new Hono()
|
|
190
|
-
private _docs: (StreamHttpRouteDoc & object)[] = []
|
|
191
|
-
private _skipped: { name: string; reason: string }[] = []
|
|
192
|
-
|
|
193
|
-
get app(): Hono {
|
|
194
|
-
return this._app
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
get docs(): StreamHttpRouteDoc[] {
|
|
198
|
-
return this._docs
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Procedures that were skipped at `build()` time because they don't fit
|
|
203
|
-
* this builder (e.g. non-streaming procedures registered against the
|
|
204
|
-
* stream builder). Surfaced via `DocSource.skippedProcedures` so
|
|
205
|
-
* DocRegistry can warn about coverage gaps.
|
|
206
|
-
*/
|
|
207
|
-
get skippedProcedures(): { name: string; reason: string }[] {
|
|
208
|
-
return this._skipped
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Registers a procedure factory with its context.
|
|
213
|
-
* Only streaming procedures (created with CreateStream) will be registered.
|
|
214
|
-
*/
|
|
215
|
-
register<TFactory extends ProceduresFactory>(
|
|
216
|
-
factory: TFactory,
|
|
217
|
-
factoryContext:
|
|
218
|
-
| ExtractContext<TFactory>
|
|
219
|
-
| ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>),
|
|
220
|
-
options?: {
|
|
221
|
-
streamMode?: StreamMode
|
|
222
|
-
extendProcedureDoc?: (params: {
|
|
223
|
-
base: StreamHttpRouteDoc
|
|
224
|
-
procedure: TStreamProcedureRegistration<any, ExtractConfig<TFactory>>
|
|
225
|
-
}) => Record<string, any>
|
|
226
|
-
}
|
|
227
|
-
): this {
|
|
228
|
-
this.factories.push({
|
|
229
|
-
factory,
|
|
230
|
-
factoryContext,
|
|
231
|
-
streamMode: options?.streamMode,
|
|
232
|
-
extendProcedureDoc: options?.extendProcedureDoc,
|
|
233
|
-
} as HonoStreamFactoryItem<any>)
|
|
234
|
-
return this
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Creates a route handler for streaming procedures.
|
|
239
|
-
*/
|
|
240
|
-
private createStreamHandler(
|
|
241
|
-
procedure: TStreamProcedureRegistration,
|
|
242
|
-
factoryContext: HonoStreamFactoryItem['factoryContext'],
|
|
243
|
-
streamMode: StreamMode
|
|
244
|
-
) {
|
|
245
|
-
return async (c: Context) => {
|
|
246
|
-
try {
|
|
247
|
-
const context =
|
|
248
|
-
typeof factoryContext === 'function' ? await factoryContext(c) : factoryContext
|
|
249
|
-
|
|
250
|
-
// GET: query params, POST: JSON body
|
|
251
|
-
const params =
|
|
252
|
-
c.req.method === 'GET'
|
|
253
|
-
? Object.fromEntries(new URL(c.req.url).searchParams)
|
|
254
|
-
: await c.req.json().catch(() => ({}))
|
|
255
|
-
|
|
256
|
-
// Validate params BEFORE starting the stream. Throw so both the
|
|
257
|
-
// validation path and the outer-catch (context resolution) path flow
|
|
258
|
-
// through one dispatch site.
|
|
259
|
-
if (procedure.config.validation?.params) {
|
|
260
|
-
const { errors } = procedure.config.validation.params(params)
|
|
261
|
-
if (errors) {
|
|
262
|
-
throw new ProcedureValidationError(
|
|
263
|
-
procedure.name,
|
|
264
|
-
`Validation error for ${procedure.name}`,
|
|
265
|
-
errors
|
|
266
|
-
)
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (this.config?.onStreamStart) {
|
|
271
|
-
this.config.onStreamStart(procedure, c, streamMode)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (streamMode === 'sse') {
|
|
275
|
-
return this.handleSSEStream(procedure, context, params, c)
|
|
276
|
-
} else {
|
|
277
|
-
return this.handleTextStream(procedure, context, params, c)
|
|
278
|
-
}
|
|
279
|
-
} catch (error) {
|
|
280
|
-
// Observer fires first — cross-cutting, cannot alter dispatch or the
|
|
281
|
-
// response. Swallow any throw from the observer so the primary flow
|
|
282
|
-
// never breaks because of instrumentation.
|
|
283
|
-
if (this.config?.onRequestError) {
|
|
284
|
-
try {
|
|
285
|
-
await this.config.onRequestError({ err: error, procedure, raw: c })
|
|
286
|
-
} catch (observerErr) {
|
|
287
|
-
console.error('[ts-procedures hono-stream] onRequestError threw — swallowed:', observerErr)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Dispatch: taxonomy → onError → hard default. The two modes are
|
|
292
|
-
// peers; developers configure whichever fits their app.
|
|
293
|
-
if (this.config?.errors || this.config?.unknownError) {
|
|
294
|
-
const resolved = resolveErrorResponse({
|
|
295
|
-
err: error,
|
|
296
|
-
userTaxonomy: this.config.errors,
|
|
297
|
-
unknownError: this.config.unknownError,
|
|
298
|
-
procedure,
|
|
299
|
-
raw: c,
|
|
300
|
-
})
|
|
301
|
-
if (resolved) {
|
|
302
|
-
await resolved.runOnCatch()
|
|
303
|
-
return c.json(resolved.body, resolved.statusCode as never)
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (this.config?.onError) {
|
|
307
|
-
return this.config.onError(procedure, c, error as Error)
|
|
308
|
-
}
|
|
309
|
-
return c.json({ error: (error as Error).message }, 500)
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Handles SSE streaming mode.
|
|
316
|
-
*/
|
|
317
|
-
private handleSSEStream(
|
|
318
|
-
procedure: TStreamProcedureRegistration,
|
|
319
|
-
context: any,
|
|
320
|
-
params: any,
|
|
321
|
-
c: Context
|
|
322
|
-
) {
|
|
323
|
-
return streamSSE(c, async (stream) => {
|
|
324
|
-
// Pass isPrevalidated: true since we already validated params in createStreamHandler
|
|
325
|
-
const generator = procedure.handler({ ...context, signal: c.req.raw.signal, isPrevalidated: true }, params)
|
|
326
|
-
|
|
327
|
-
stream.onAbort(async () => {
|
|
328
|
-
await generator.return(undefined)
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
let eventId = 0
|
|
332
|
-
try {
|
|
333
|
-
const iterator = generator[Symbol.asyncIterator]()
|
|
334
|
-
let iterResult = await iterator.next()
|
|
335
|
-
|
|
336
|
-
while (!iterResult.done) {
|
|
337
|
-
const value = iterResult.value
|
|
338
|
-
const currentId = eventId++
|
|
339
|
-
const meta = getSSEMeta(value)
|
|
340
|
-
|
|
341
|
-
const data =
|
|
342
|
-
typeof value === 'string'
|
|
343
|
-
? value
|
|
344
|
-
: value != null
|
|
345
|
-
? JSON.stringify(value)
|
|
346
|
-
: ''
|
|
347
|
-
|
|
348
|
-
await stream.writeSSE({
|
|
349
|
-
data,
|
|
350
|
-
event: meta?.event ?? procedure.name,
|
|
351
|
-
id: meta?.id ?? String(currentId),
|
|
352
|
-
...(meta?.retry !== undefined && { retry: meta.retry }),
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
iterResult = await iterator.next()
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Send return value as a special 'return' event (if present)
|
|
359
|
-
if (iterResult.value !== undefined) {
|
|
360
|
-
const returnData =
|
|
361
|
-
typeof iterResult.value === 'string'
|
|
362
|
-
? iterResult.value
|
|
363
|
-
: JSON.stringify(iterResult.value)
|
|
364
|
-
|
|
365
|
-
await stream.writeSSE({
|
|
366
|
-
data: returnData,
|
|
367
|
-
event: 'return',
|
|
368
|
-
id: String(eventId++),
|
|
369
|
-
})
|
|
370
|
-
}
|
|
371
|
-
} catch (error) {
|
|
372
|
-
// Dispatch order mirrors hono-rpc: taxonomy → onMidStreamError → default.
|
|
373
|
-
// The HTTP status is already committed (200 OK headers were sent the
|
|
374
|
-
// moment streaming started), so the taxonomy here only drives the
|
|
375
|
-
// wire-protocol body shape — clients dispatch through the same error
|
|
376
|
-
// registry as RPC/API responses by reading `data.name`.
|
|
377
|
-
let errorData: unknown
|
|
378
|
-
let sseEventOverride: string | undefined
|
|
379
|
-
let sseIdOverride: string | undefined
|
|
380
|
-
let sseRetryOverride: number | undefined
|
|
381
|
-
let runOnCatch: (() => Promise<void>) | undefined
|
|
382
|
-
|
|
383
|
-
if (this.config?.errors || this.config?.unknownError) {
|
|
384
|
-
const resolved = resolveErrorResponse({
|
|
385
|
-
err: error,
|
|
386
|
-
userTaxonomy: this.config.errors,
|
|
387
|
-
unknownError: this.config.unknownError,
|
|
388
|
-
procedure,
|
|
389
|
-
raw: c,
|
|
390
|
-
})
|
|
391
|
-
if (resolved) {
|
|
392
|
-
errorData = resolved.body
|
|
393
|
-
sseEventOverride = 'error'
|
|
394
|
-
runOnCatch = resolved.runOnCatch
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (errorData === undefined && this.config?.onMidStreamError) {
|
|
399
|
-
const errorResult: MidStreamErrorResult<TErrorData> | undefined =
|
|
400
|
-
this.config.onMidStreamError(procedure, c, error as Error)
|
|
401
|
-
if (errorResult?.data !== undefined) {
|
|
402
|
-
errorData = errorResult.data
|
|
403
|
-
sseEventOverride = procedure.name
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (errorData === undefined) {
|
|
408
|
-
errorData = { error: (error as Error).message }
|
|
409
|
-
sseEventOverride = 'error'
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const sseMeta = getSSEMeta(errorData)
|
|
413
|
-
|
|
414
|
-
await stream.writeSSE({
|
|
415
|
-
data: typeof errorData === 'string' ? errorData : JSON.stringify(errorData),
|
|
416
|
-
event: sseMeta?.event ?? sseEventOverride ?? 'error',
|
|
417
|
-
id: sseMeta?.id ?? sseIdOverride ?? String(eventId++),
|
|
418
|
-
...((sseMeta?.retry ?? sseRetryOverride) !== undefined && {
|
|
419
|
-
retry: (sseMeta?.retry ?? sseRetryOverride) as number,
|
|
420
|
-
}),
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
if (runOnCatch) {
|
|
424
|
-
await runOnCatch()
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// closeStream defaults to true if not specified
|
|
428
|
-
// (stream closes naturally after this handler completes)
|
|
429
|
-
} finally {
|
|
430
|
-
if (this.config?.onStreamEnd) {
|
|
431
|
-
this.config.onStreamEnd(procedure, c, 'sse')
|
|
432
|
-
}
|
|
433
|
-
if (this.config?.onRequestEnd) {
|
|
434
|
-
this.config.onRequestEnd(c)
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
})
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Handles text streaming mode.
|
|
442
|
-
*/
|
|
443
|
-
private handleTextStream(
|
|
444
|
-
procedure: TStreamProcedureRegistration,
|
|
445
|
-
context: any,
|
|
446
|
-
params: any,
|
|
447
|
-
c: Context
|
|
448
|
-
) {
|
|
449
|
-
return streamText(c, async (stream) => {
|
|
450
|
-
// Pass isPrevalidated: true since we already validated params in createStreamHandler
|
|
451
|
-
const generator = procedure.handler({ ...context, signal: c.req.raw.signal, isPrevalidated: true }, params)
|
|
452
|
-
|
|
453
|
-
stream.onAbort(async () => {
|
|
454
|
-
await generator.return(undefined)
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
try {
|
|
458
|
-
for await (const value of generator) {
|
|
459
|
-
await stream.writeln(JSON.stringify(value))
|
|
460
|
-
}
|
|
461
|
-
} catch (error) {
|
|
462
|
-
// Same dispatch order as SSE — taxonomy first, onMidStreamError next,
|
|
463
|
-
// hard default last. Text streams have no event/id metadata, so we
|
|
464
|
-
// only forward the body bytes.
|
|
465
|
-
let errorData: unknown
|
|
466
|
-
let runOnCatch: (() => Promise<void>) | undefined
|
|
467
|
-
|
|
468
|
-
if (this.config?.errors || this.config?.unknownError) {
|
|
469
|
-
const resolved = resolveErrorResponse({
|
|
470
|
-
err: error,
|
|
471
|
-
userTaxonomy: this.config.errors,
|
|
472
|
-
unknownError: this.config.unknownError,
|
|
473
|
-
procedure,
|
|
474
|
-
raw: c,
|
|
475
|
-
})
|
|
476
|
-
if (resolved) {
|
|
477
|
-
errorData = resolved.body
|
|
478
|
-
runOnCatch = resolved.runOnCatch
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (errorData === undefined && this.config?.onMidStreamError) {
|
|
483
|
-
const errorResult: MidStreamErrorResult<TErrorData> | undefined =
|
|
484
|
-
this.config.onMidStreamError(procedure, c, error as Error)
|
|
485
|
-
if (errorResult?.data !== undefined) {
|
|
486
|
-
errorData = errorResult.data
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (errorData === undefined) {
|
|
491
|
-
errorData = { error: (error as Error).message }
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
await stream.writeln(JSON.stringify(errorData))
|
|
495
|
-
|
|
496
|
-
if (runOnCatch) {
|
|
497
|
-
await runOnCatch()
|
|
498
|
-
}
|
|
499
|
-
} finally {
|
|
500
|
-
if (this.config?.onStreamEnd) {
|
|
501
|
-
this.config.onStreamEnd(procedure, c, 'text')
|
|
502
|
-
}
|
|
503
|
-
if (this.config?.onRequestEnd) {
|
|
504
|
-
this.config.onRequestEnd(c)
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
})
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Builds and returns the Hono application with registered streaming routes.
|
|
512
|
-
*/
|
|
513
|
-
build(): Hono {
|
|
514
|
-
this.factories.forEach(({ factory, factoryContext, streamMode, extendProcedureDoc }) => {
|
|
515
|
-
const mode = streamMode ?? this.config?.defaultStreamMode ?? 'sse'
|
|
516
|
-
|
|
517
|
-
const procedures = factory.getProcedures()
|
|
518
|
-
|
|
519
|
-
// Track non-streaming procedures so DocRegistry can warn about coverage
|
|
520
|
-
// gaps (e.g. a regular procedure registered with this builder will get
|
|
521
|
-
// dropped here and needs to be registered with HonoRPCAppBuilder).
|
|
522
|
-
for (const p of procedures as { name: string; isStream?: boolean }[]) {
|
|
523
|
-
if (p.isStream !== true) {
|
|
524
|
-
const reason =
|
|
525
|
-
'Non-streaming procedure registered with HonoStreamAppBuilder — register it with HonoRPCAppBuilder (or HonoApiAppBuilder) instead.'
|
|
526
|
-
this._skipped.push({ name: p.name, reason })
|
|
527
|
-
console.warn(
|
|
528
|
-
`[ts-procedures hono-stream] Skipping procedure "${p.name}": ${reason}`
|
|
529
|
-
)
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
procedures
|
|
534
|
-
.filter(
|
|
535
|
-
(p: { isStream?: boolean }): p is TStreamProcedureRegistration => p.isStream === true
|
|
536
|
-
)
|
|
537
|
-
.forEach((procedure: TStreamProcedureRegistration<any, RPCConfig>) => {
|
|
538
|
-
const route = this.buildStreamHttpRouteDoc(procedure, mode, extendProcedureDoc)
|
|
539
|
-
|
|
540
|
-
this._docs.push(route)
|
|
541
|
-
|
|
542
|
-
const handler = this.createStreamHandler(procedure, factoryContext, mode)
|
|
543
|
-
|
|
544
|
-
// Register both GET and POST handlers
|
|
545
|
-
this._app.get(route.path, handler)
|
|
546
|
-
this._app.post(route.path, handler)
|
|
547
|
-
})
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
return this._app
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Generates the Stream HTTP route documentation for the given procedure.
|
|
555
|
-
*/
|
|
556
|
-
private buildStreamHttpRouteDoc(
|
|
557
|
-
procedure: TStreamProcedureRegistration<any, RPCConfig>,
|
|
558
|
-
streamMode: StreamMode,
|
|
559
|
-
extendProcedureDoc?: HonoStreamFactoryItem['extendProcedureDoc']
|
|
560
|
-
): StreamHttpRouteDoc {
|
|
561
|
-
const { config } = procedure
|
|
562
|
-
const path = HonoStreamAppBuilder.makeStreamHttpRoutePath({
|
|
563
|
-
name: procedure.name,
|
|
564
|
-
config,
|
|
565
|
-
prefix: this.config?.pathPrefix,
|
|
566
|
-
})
|
|
567
|
-
// POST first so codegen (which uses `methods[0]`) defaults to POST. POST is
|
|
568
|
-
// the canonical method for streaming procedures because it can carry a body
|
|
569
|
-
// for params; GET is the supplementary method for query-string callers.
|
|
570
|
-
const methods = ['post', 'get'] as const
|
|
571
|
-
const jsonSchema: { params?: Record<string, unknown>; yieldType?: Record<string, unknown>; returnType?: Record<string, unknown> } = {}
|
|
572
|
-
|
|
573
|
-
if (config.schema?.params) {
|
|
574
|
-
jsonSchema.params = config.schema.params
|
|
575
|
-
}
|
|
576
|
-
if (streamMode === 'sse') {
|
|
577
|
-
jsonSchema.yieldType = {
|
|
578
|
-
type: 'object',
|
|
579
|
-
description: 'SSE message envelope. The data field contains the procedure yield value.',
|
|
580
|
-
required: ['data', 'event', 'id'],
|
|
581
|
-
properties: {
|
|
582
|
-
data: config.schema?.yieldType ?? {},
|
|
583
|
-
event: { type: 'string' },
|
|
584
|
-
id: { type: 'string' },
|
|
585
|
-
retry: { type: 'number' },
|
|
586
|
-
},
|
|
587
|
-
}
|
|
588
|
-
} else if (config.schema?.yieldType) {
|
|
589
|
-
// Text mode: pass through as-is
|
|
590
|
-
jsonSchema.yieldType = config.schema.yieldType
|
|
591
|
-
}
|
|
592
|
-
if (config.schema?.returnType) {
|
|
593
|
-
jsonSchema.returnType = config.schema.returnType
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
const base: StreamHttpRouteDoc = {
|
|
597
|
-
kind: 'stream',
|
|
598
|
-
name: procedure.name,
|
|
599
|
-
version: config.version,
|
|
600
|
-
scope: config.scope,
|
|
601
|
-
path,
|
|
602
|
-
methods: [...methods],
|
|
603
|
-
streamMode,
|
|
604
|
-
jsonSchema,
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (config.errors && config.errors.length > 0) {
|
|
608
|
-
base.errors = [...config.errors]
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
let extendedDoc: object = {}
|
|
612
|
-
|
|
613
|
-
if (extendProcedureDoc) {
|
|
614
|
-
extendedDoc = extendProcedureDoc({ base, procedure })
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
return {
|
|
618
|
-
...extendedDoc,
|
|
619
|
-
...base,
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Context } from 'hono'
|
|
2
|
-
import { TStreamProcedureRegistration } from '../../../index.js'
|
|
3
|
-
import { ExtractConfig, ExtractContext } from '../../types.js'
|
|
4
|
-
|
|
5
|
-
export type { StreamHttpRouteDoc } from '../../types.js'
|
|
6
|
-
export type { StreamMode } from '../../types.js'
|
|
7
|
-
|
|
8
|
-
import type { StreamHttpRouteDoc, StreamMode } from '../../types.js'
|
|
9
|
-
|
|
10
|
-
export type HonoStreamFactoryItem<TFactory = any> = {
|
|
11
|
-
factory: TFactory
|
|
12
|
-
factoryContext:
|
|
13
|
-
| ExtractContext<TFactory>
|
|
14
|
-
| ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>)
|
|
15
|
-
streamMode?: StreamMode
|
|
16
|
-
extendProcedureDoc?: (params: {
|
|
17
|
-
base: StreamHttpRouteDoc
|
|
18
|
-
procedure: TStreamProcedureRegistration<any, ExtractConfig<TFactory>>
|
|
19
|
-
}) => Record<string, any>
|
|
20
|
-
}
|
|
File without changes
|
|
File without changes
|
/package/build/{implementations/http/hono-api/error-taxonomy.test.d.ts → create-stream.test.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|
/package/build/implementations/http/{hono-rpc/error-taxonomy.test.d.ts → error-dispatch.test.d.ts}
RENAMED
|
File without changes
|
/package/build/implementations/http/{hono-rpc/index.test.d.ts → hono/handlers/http-stream.test.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|
/package/build/implementations/http/{hono-stream/index.test.d.ts → hono/handlers/rpc.test.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|