ts-procedures 7.3.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 +104 -53
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +205 -232
- 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 +34 -48
- package/agent_config/cursor/cursorrules +34 -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 +124 -124
- package/build/index.js +10 -221
- package/build/index.js.map +1 -1
- package/build/index.test.js +20 -919
- 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 +15 -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 +22 -1249
- package/src/index.ts +49 -485
- 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
package/src/create.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { ProcedureError, ProcedureRegistrationError, ProcedureValidationError } from './errors.js'
|
|
2
|
+
import { computeSchema } from './schema/compute-schema.js'
|
|
3
|
+
import { Prettify, TSchemaLib } from './schema/types.js'
|
|
4
|
+
import { captureDefinitionInfo } from './stack-utils.js'
|
|
5
|
+
import {
|
|
6
|
+
TBuilderConfig,
|
|
7
|
+
THttpProcedureRegistration,
|
|
8
|
+
THttpStreamProcedureRegistration,
|
|
9
|
+
TLocalContext,
|
|
10
|
+
TProcedureRegistration,
|
|
11
|
+
TStreamProcedureRegistration,
|
|
12
|
+
} from './types.js'
|
|
13
|
+
|
|
14
|
+
export type CreateBuilderArg<TContext, TExtendedConfig> = {
|
|
15
|
+
config?: TBuilderConfig
|
|
16
|
+
onCreate?: (
|
|
17
|
+
procedure:
|
|
18
|
+
| TProcedureRegistration<TContext, TExtendedConfig>
|
|
19
|
+
| TStreamProcedureRegistration<TContext, TExtendedConfig>
|
|
20
|
+
) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function makeCreate<TContext, TExtendedConfig>(
|
|
24
|
+
procedures: Map<
|
|
25
|
+
string,
|
|
26
|
+
| TProcedureRegistration<TContext, TExtendedConfig>
|
|
27
|
+
| TStreamProcedureRegistration<TContext, TExtendedConfig>
|
|
28
|
+
| THttpProcedureRegistration<TContext>
|
|
29
|
+
| THttpStreamProcedureRegistration<TContext>
|
|
30
|
+
>,
|
|
31
|
+
builder?: CreateBuilderArg<TContext, TExtendedConfig>
|
|
32
|
+
) {
|
|
33
|
+
return function Create<TName extends string, TParams, TReturnType>(
|
|
34
|
+
name: TName,
|
|
35
|
+
config: {
|
|
36
|
+
description?: string
|
|
37
|
+
schema?: {
|
|
38
|
+
params?: TParams
|
|
39
|
+
returnType?: TReturnType
|
|
40
|
+
}
|
|
41
|
+
} & TExtendedConfig,
|
|
42
|
+
handler: (
|
|
43
|
+
ctx: Prettify<TContext & TLocalContext>,
|
|
44
|
+
params: TSchemaLib<TParams>
|
|
45
|
+
) => Promise<TSchemaLib<TReturnType>>
|
|
46
|
+
) {
|
|
47
|
+
// Capture definition location as first action
|
|
48
|
+
const definitionInfo = captureDefinitionInfo()
|
|
49
|
+
|
|
50
|
+
const HTTP_FIELDS = ['path', 'method', 'req', 'res', 'successStatus'] as const
|
|
51
|
+
const presentHttpFields = HTTP_FIELDS.filter((f) => (config as any)[f] !== undefined)
|
|
52
|
+
if (presentHttpFields.length > 0) {
|
|
53
|
+
throw new ProcedureRegistrationError(
|
|
54
|
+
name,
|
|
55
|
+
`HTTP fields require CreateHttp / CreateHttpStream. Procedure "${name}" has [${presentHttpFields.join(', ')}] which are not valid on Create.`,
|
|
56
|
+
definitionInfo,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// BEFORE computeSchema - fail fast on duplicate
|
|
61
|
+
if (procedures.has(name)) {
|
|
62
|
+
throw new Error(`Procedure with name ${name} is already registered`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
|
|
66
|
+
|
|
67
|
+
// Create error factory once at registration time (outside handler)
|
|
68
|
+
const errorFactory = (message: string, meta?: object) =>
|
|
69
|
+
new ProcedureError(name, message, meta, definitionInfo)
|
|
70
|
+
|
|
71
|
+
const registeredProcedure: TProcedureRegistration<TContext, TExtendedConfig> = {
|
|
72
|
+
name,
|
|
73
|
+
kind: 'rpc',
|
|
74
|
+
config: {
|
|
75
|
+
...config,
|
|
76
|
+
description: config.description,
|
|
77
|
+
schema: jsonSchema,
|
|
78
|
+
validation: {
|
|
79
|
+
params: validations.params,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
handler: async (ctx: Prettify<TContext>, params: TSchemaLib<TParams>) => {
|
|
84
|
+
try {
|
|
85
|
+
// Skip validation if caller has already validated or if builder config dictates noRuntimeValidation
|
|
86
|
+
const skipValidation =
|
|
87
|
+
(ctx as { isPrevalidated?: boolean }).isPrevalidated ||
|
|
88
|
+
builder?.config?.noRuntimeValidation
|
|
89
|
+
|
|
90
|
+
if (validations?.params && !skipValidation) {
|
|
91
|
+
const { errors } = validations.params(params)
|
|
92
|
+
|
|
93
|
+
if (errors) {
|
|
94
|
+
throw new ProcedureValidationError(
|
|
95
|
+
name,
|
|
96
|
+
`Validation error for ${name}`,
|
|
97
|
+
errors,
|
|
98
|
+
definitionInfo
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const localCtx: TLocalContext = {
|
|
104
|
+
error: errorFactory,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// params is correctly typed at the public API boundary;
|
|
108
|
+
// cast here because TS cannot narrow generics inside implementations
|
|
109
|
+
return await handler(
|
|
110
|
+
{ ...ctx, ...localCtx } as Prettify<TContext & TLocalContext>,
|
|
111
|
+
params as any
|
|
112
|
+
)
|
|
113
|
+
} catch (error: any) {
|
|
114
|
+
if (error instanceof ProcedureError) throw error
|
|
115
|
+
const err = new ProcedureError(
|
|
116
|
+
name,
|
|
117
|
+
`Error in handler for ${name} - ${error?.message}`,
|
|
118
|
+
undefined,
|
|
119
|
+
definitionInfo
|
|
120
|
+
)
|
|
121
|
+
err.cause = error
|
|
122
|
+
// Preserve original stack but append definition info
|
|
123
|
+
if (error.stack && definitionInfo.definedAt) {
|
|
124
|
+
const { file, line, column } = definitionInfo.definedAt
|
|
125
|
+
err.stack =
|
|
126
|
+
error.stack +
|
|
127
|
+
`\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
128
|
+
} else if (error.stack) {
|
|
129
|
+
err.stack = error.stack
|
|
130
|
+
}
|
|
131
|
+
throw err
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
procedures.set(name, registeredProcedure)
|
|
137
|
+
builder?.onCreate?.(registeredProcedure)
|
|
138
|
+
|
|
139
|
+
const info = {
|
|
140
|
+
name,
|
|
141
|
+
...registeredProcedure.config,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// return so can be called directly (ie: int/unit tests)
|
|
145
|
+
return {
|
|
146
|
+
[name]: registeredProcedure.handler,
|
|
147
|
+
procedure: registeredProcedure.handler,
|
|
148
|
+
info,
|
|
149
|
+
} as {
|
|
150
|
+
[K in TName]: (
|
|
151
|
+
ctx: Prettify<TContext>,
|
|
152
|
+
params: TSchemaLib<TParams>
|
|
153
|
+
) => Promise<TSchemaLib<TReturnType>>
|
|
154
|
+
} & {
|
|
155
|
+
procedure: (
|
|
156
|
+
ctx: Prettify<TContext>,
|
|
157
|
+
params: TSchemaLib<TParams>
|
|
158
|
+
) => Promise<TSchemaLib<TReturnType>>
|
|
159
|
+
info: {
|
|
160
|
+
name: TName
|
|
161
|
+
description?: string
|
|
162
|
+
schema: {
|
|
163
|
+
params?: TParams
|
|
164
|
+
returnType?: TReturnType
|
|
165
|
+
}
|
|
166
|
+
validation?: {
|
|
167
|
+
params?: (params: any) => { errors?: any[] }
|
|
168
|
+
}
|
|
169
|
+
} & TExtendedConfig
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
package/src/exports.ts
CHANGED
|
@@ -5,3 +5,5 @@ export * from './schema/extract-json-schema.js'
|
|
|
5
5
|
export * from './schema/parser.js'
|
|
6
6
|
export * from './schema/resolve-schema-lib.js'
|
|
7
7
|
export * from './schema/types.js'
|
|
8
|
+
export type { HttpReturn } from './create-http.js'
|
|
9
|
+
export type { TCreateHttpConfig } from './types.js'
|
|
@@ -4,59 +4,46 @@ HTTP implementation builders for `ts-procedures` that create type-safe, versione
|
|
|
4
4
|
|
|
5
5
|
## Available Implementations
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
For procedures created with `Create()` - standard request/response pattern using POST.
|
|
10
|
-
|
|
11
|
-
| Framework | Package | Description |
|
|
12
|
-
|-----------|---------|-------------|
|
|
13
|
-
| [Express RPC](./express-rpc/README.md) | `express-rpc` | Express.js integration |
|
|
14
|
-
| [Hono RPC](./hono-rpc/README.md) | `hono-rpc` | Hono integration (Bun, Deno, Cloudflare Workers, Node.js) |
|
|
15
|
-
|
|
16
|
-
### Streaming
|
|
17
|
-
|
|
18
|
-
For procedures created with `CreateStream()` - server-sent events and streaming responses.
|
|
19
|
-
|
|
20
|
-
| Framework | Package | Description |
|
|
7
|
+
| Framework | Subpath | Description |
|
|
21
8
|
|-----------|---------|-------------|
|
|
22
|
-
|
|
|
9
|
+
| HonoAppBuilder | `ts-procedures/hono` | Unified Hono builder — dispatches RPC, RPC streams, REST, and REST streams from one `register()` call. Works on Bun, Deno, Cloudflare Workers, and Node.js. |
|
|
23
10
|
|
|
24
|
-
|
|
11
|
+
`HonoAppBuilder` is a unified builder: one builder, one `app`, one set of cross-cutting hooks. The procedure kind drives behavior — pick the creator that matches the shape you need.
|
|
25
12
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
| Framework | Package | Description |
|
|
29
|
-
|-----------|---------|-------------|
|
|
30
|
-
| [Hono API](./hono-api/) | `hono-api` | REST-style routing by HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD) |
|
|
13
|
+
### Procedure kinds and which creator to use
|
|
31
14
|
|
|
32
|
-
|
|
15
|
+
| Procedure kind | Created with | Config shape | Handler return | HTTP methods | Use case |
|
|
16
|
+
|----------------|--------------|--------------|----------------|--------------|----------|
|
|
17
|
+
| `rpc` | `Create()` | `RPCConfig` (`scope`, `version`) | `Promise<T>` | POST | Standard request/response |
|
|
18
|
+
| `rpc-stream` | `CreateStream()` | `RPCConfig` | `AsyncGenerator<T>` | GET, POST | RPC-style streaming (SSE / text) |
|
|
19
|
+
| `http` | `CreateHttp()` | `APIConfig` (`path`, `method`) | `Promise<T>` | GET, POST, PUT, DELETE, PATCH, HEAD | REST-style endpoint with `schema.req` channels |
|
|
20
|
+
| `http-stream` | `CreateHttpStream()` | `APIConfig` | `AsyncGenerator<T>` | GET, POST | REST-style streaming endpoint |
|
|
33
21
|
|
|
34
|
-
|
|
35
|
-
|------|--------------|----------------|--------------|----------|
|
|
36
|
-
| RPC | `Create()` | `Promise<T>` | POST | Standard request/response |
|
|
37
|
-
| Stream | `CreateStream()` | `AsyncGenerator<T>` | GET, POST | Real-time updates, SSE |
|
|
38
|
-
| API | `Create()` | `Promise<T>` | GET, POST, PUT, DELETE, PATCH, HEAD | REST-style endpoints with per-channel input |
|
|
22
|
+
`HonoAppBuilder` accepts all four kinds from the same factory.
|
|
39
23
|
|
|
40
24
|
## Core Concepts
|
|
41
25
|
|
|
42
|
-
### Config
|
|
26
|
+
### Config Interfaces
|
|
43
27
|
|
|
44
|
-
|
|
28
|
+
RPC and RPC-stream procedures share `RPCConfig`:
|
|
45
29
|
|
|
46
30
|
```typescript
|
|
47
31
|
interface RPCConfig {
|
|
48
32
|
scope: string | string[] // Route path segment(s)
|
|
49
33
|
version: number // API version number
|
|
34
|
+
errors?: string[] // Optional taxonomy keys for typed-client narrowing
|
|
50
35
|
}
|
|
51
36
|
```
|
|
52
37
|
|
|
53
|
-
|
|
38
|
+
REST-style procedures (`CreateHttp` / `CreateHttpStream`) use `APIConfig`:
|
|
54
39
|
|
|
55
40
|
```typescript
|
|
56
41
|
interface APIConfig {
|
|
57
42
|
path: string // Route path with Hono params (e.g., '/users/:id')
|
|
58
43
|
method: HttpMethod // 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
|
|
59
44
|
successStatus?: number // Default: POST→201, DELETE→204, others→200
|
|
45
|
+
scope?: string // Optional grouping for codegen file emission
|
|
46
|
+
errors?: string[]
|
|
60
47
|
}
|
|
61
48
|
```
|
|
62
49
|
|
|
@@ -110,71 +97,94 @@ builder.register(Factory, async (c) => {
|
|
|
110
97
|
|
|
111
98
|
All HTTP implementations automatically inject an `AbortSignal` into the handler context as `ctx.signal`. This signal aborts when the client disconnects, enabling handlers to cancel in-flight work (fetch calls, database queries, etc.).
|
|
112
99
|
|
|
113
|
-
| Framework | Signal
|
|
114
|
-
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
| Express RPC | Lazy `AbortController` | Created on first `ctx.signal` access, wired to `req.on('close')` |
|
|
100
|
+
| Framework | Procedure kind | Signal source | Behavior |
|
|
101
|
+
|-----------|---------------|---------------|----------|
|
|
102
|
+
| HonoAppBuilder | `rpc`, `http` | `c.req.raw.signal` | Web standard Request signal |
|
|
103
|
+
| HonoAppBuilder | `rpc-stream`, `http-stream` | `c.req.raw.signal` | Combined with internal stream AbortController via `AbortSignal.any()` |
|
|
118
104
|
|
|
119
105
|
For streaming procedures, `signal.reason` is `'stream-completed'` on normal completion, allowing handlers to distinguish from client disconnection.
|
|
120
106
|
|
|
121
107
|
### Error Handling
|
|
122
108
|
|
|
123
|
-
|
|
109
|
+
Both builders support two peer error-handling modes — **declarative** (`errors` taxonomy + `unknownError`) and **imperative** (`onError` callback) — plus a cross-cutting `onRequestError` observer for logging, tracing, and metrics.
|
|
124
110
|
|
|
125
111
|
Full spec (taxonomy shape, `toResponse`/`onCatch`/`match`, per-route narrowing, mid-stream caveats): **[docs/http-integrations.md § Error Handling](../../../docs/http-integrations.md#error-handling)**.
|
|
126
112
|
|
|
127
113
|
### Lifecycle Hooks
|
|
128
114
|
|
|
129
|
-
**
|
|
115
|
+
`HonoAppBuilder`'s config is **stratified**: cross-cutting hooks live at the top level, kind-specific hooks live inside `rpc:`, `api:`, and `stream:` blocks.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
new HonoAppBuilder({
|
|
119
|
+
// Cross-cutting (every kind)
|
|
120
|
+
onRequestStart, onRequestEnd, onRequestError,
|
|
121
|
+
errors, unknownError, onError,
|
|
122
|
+
|
|
123
|
+
// Kind-specific blocks
|
|
124
|
+
rpc: { onSuccess },
|
|
125
|
+
api: { queryParser, onSuccess },
|
|
126
|
+
stream: { defaultStreamMode, onStreamStart, onStreamEnd, onMidStreamError },
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Non-streaming flow (`rpc`, `http`):**
|
|
130
131
|
|
|
131
132
|
```
|
|
132
133
|
onRequestStart → handler → onSuccess → onRequestEnd
|
|
133
134
|
↓
|
|
134
135
|
(on error)
|
|
135
136
|
↓
|
|
136
|
-
|
|
137
|
+
onRequestError (observer)
|
|
138
|
+
↓
|
|
139
|
+
errors taxonomy / onError fallback
|
|
137
140
|
↓
|
|
138
141
|
onRequestEnd
|
|
139
142
|
```
|
|
140
143
|
|
|
141
|
-
**
|
|
144
|
+
**Streaming flow (`rpc-stream`, `http-stream`):**
|
|
142
145
|
|
|
143
146
|
```
|
|
144
147
|
onRequestStart → onStreamStart → [yields...] → onStreamEnd → onRequestEnd
|
|
145
148
|
↓
|
|
146
149
|
(on error)
|
|
147
150
|
↓
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
┌──────────────────┴──────────────────┐
|
|
152
|
+
↓ (pre-stream — before onStreamStart) ↓ (mid-stream — after first yield)
|
|
153
|
+
errors taxonomy / onError → onRequestEnd onMidStreamError → onStreamEnd → onRequestEnd
|
|
151
154
|
```
|
|
152
155
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
|
156
|
-
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
156
|
+
Pre-stream errors short-circuit before `onStreamStart` fires, so `onStreamEnd` does **not** run on that path. Mid-stream errors run `onMidStreamError` and then still fire `onStreamEnd` because the stream had already started.
|
|
157
|
+
|
|
158
|
+
| Hook | Config block | Triggers on |
|
|
159
|
+
|------|--------------|-------------|
|
|
160
|
+
| `onRequestStart` | top-level | Before route handler (every kind) |
|
|
161
|
+
| `onRequestEnd` | top-level | After response sent (every kind) |
|
|
162
|
+
| `onRequestError` | top-level | Cross-cutting observer — fires for every caught error before dispatch |
|
|
163
|
+
| `onError` | top-level | Imperative pre-stream error callback — peer of `errors` taxonomy |
|
|
164
|
+
| `errors` / `unknownError` | top-level | Declarative taxonomy dispatch |
|
|
165
|
+
| `rpc.onSuccess` | `rpc:` | After successful RPC handler |
|
|
166
|
+
| `api.onSuccess` | `api:` | After successful HTTP handler |
|
|
167
|
+
| `api.queryParser` | `api:` | Custom query-string parser for HTTP routes |
|
|
168
|
+
| `stream.defaultStreamMode` | `stream:` | Default mode (`'sse'` \| `'text'`) for both stream kinds |
|
|
169
|
+
| `stream.onStreamStart` | `stream:` | Before first yield |
|
|
170
|
+
| `stream.onStreamEnd` | `stream:` | After stream completes |
|
|
171
|
+
| `stream.onMidStreamError` | `stream:` | On mid-stream error (generator throws after first yield) |
|
|
164
172
|
|
|
165
173
|
### Route Documentation
|
|
166
174
|
|
|
167
|
-
Each registered procedure generates documentation accessible via `builder.docs`.
|
|
175
|
+
Each registered procedure generates documentation accessible via `builder.docs`. Every doc carries a `kind` discriminant for reliable narrowing.
|
|
168
176
|
|
|
169
|
-
**RPC
|
|
177
|
+
**RPC (`RPCHttpRouteDoc`, `kind: 'rpc'`):**
|
|
170
178
|
|
|
171
179
|
```typescript
|
|
172
180
|
interface RPCHttpRouteDoc {
|
|
181
|
+
kind: 'rpc'
|
|
173
182
|
name: string
|
|
174
183
|
path: string
|
|
175
184
|
method: 'post'
|
|
176
185
|
scope: string | string[]
|
|
177
186
|
version: number
|
|
187
|
+
errors?: string[]
|
|
178
188
|
jsonSchema: {
|
|
179
189
|
body?: object // From schema.params
|
|
180
190
|
response?: object // From schema.returnType
|
|
@@ -182,16 +192,18 @@ interface RPCHttpRouteDoc {
|
|
|
182
192
|
}
|
|
183
193
|
```
|
|
184
194
|
|
|
185
|
-
**
|
|
195
|
+
**RPC stream (`StreamHttpRouteDoc`, `kind: 'stream'`):**
|
|
186
196
|
|
|
187
197
|
```typescript
|
|
188
198
|
interface StreamHttpRouteDoc {
|
|
199
|
+
kind: 'stream'
|
|
189
200
|
name: string
|
|
190
201
|
path: string
|
|
191
202
|
methods: ('get' | 'post')[]
|
|
192
203
|
streamMode: 'sse' | 'text'
|
|
193
204
|
scope: string | string[]
|
|
194
205
|
version: number
|
|
206
|
+
errors?: string[]
|
|
195
207
|
jsonSchema: {
|
|
196
208
|
params?: object // From schema.params
|
|
197
209
|
yieldType?: object // From schema.yieldType
|
|
@@ -200,31 +212,65 @@ interface StreamHttpRouteDoc {
|
|
|
200
212
|
}
|
|
201
213
|
```
|
|
202
214
|
|
|
203
|
-
**
|
|
215
|
+
**REST (`APIHttpRouteDoc`, `kind: 'api'`):**
|
|
204
216
|
|
|
205
217
|
```typescript
|
|
206
218
|
interface APIHttpRouteDoc {
|
|
219
|
+
kind: 'api'
|
|
207
220
|
name: string
|
|
221
|
+
scope?: string
|
|
208
222
|
path: string
|
|
209
223
|
method: HttpMethod
|
|
210
|
-
fullPath: string
|
|
224
|
+
fullPath: string // Includes pathPrefix
|
|
211
225
|
successStatus?: number
|
|
226
|
+
errors?: string[]
|
|
212
227
|
jsonSchema: {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
228
|
+
req?: {
|
|
229
|
+
pathParams?: object // From schema.req.pathParams
|
|
230
|
+
query?: object // From schema.req.query
|
|
231
|
+
body?: object // From schema.req.body
|
|
232
|
+
headers?: object // From schema.req.headers
|
|
233
|
+
}
|
|
234
|
+
res?: {
|
|
235
|
+
body?: object // From schema.res.body
|
|
236
|
+
headers?: object // From schema.res.headers
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**REST stream (`HttpStreamRouteDoc`, `kind: 'http-stream'`):**
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
interface HttpStreamRouteDoc {
|
|
246
|
+
kind: 'http-stream'
|
|
247
|
+
name: string
|
|
248
|
+
scope?: string
|
|
249
|
+
path: string
|
|
250
|
+
method: HttpMethod
|
|
251
|
+
fullPath: string
|
|
252
|
+
streamMode: 'sse' | 'text'
|
|
253
|
+
errors?: string[]
|
|
254
|
+
jsonSchema: {
|
|
255
|
+
req?: {
|
|
256
|
+
pathParams?: object
|
|
257
|
+
query?: object
|
|
258
|
+
body?: object
|
|
259
|
+
headers?: object
|
|
260
|
+
}
|
|
261
|
+
res?: { headers?: object }
|
|
262
|
+
yield?: object
|
|
263
|
+
returnType?: object
|
|
218
264
|
}
|
|
219
265
|
}
|
|
220
266
|
```
|
|
221
267
|
|
|
222
268
|
### Builder Pattern
|
|
223
269
|
|
|
224
|
-
|
|
270
|
+
Both builders follow the same pattern:
|
|
225
271
|
|
|
226
272
|
```typescript
|
|
227
|
-
const builder = new
|
|
273
|
+
const builder = new HonoAppBuilder(config)
|
|
228
274
|
.register(PublicFactory, publicContextResolver)
|
|
229
275
|
.register(ProtectedFactory, protectedContextResolver)
|
|
230
276
|
|
|
@@ -232,9 +278,9 @@ const app = builder.build()
|
|
|
232
278
|
const docs = builder.docs
|
|
233
279
|
```
|
|
234
280
|
|
|
235
|
-
|
|
281
|
+
`build()` is synchronous — it returns the framework app instance directly. Don't `await` the call.
|
|
236
282
|
|
|
237
|
-
|
|
283
|
+
A single `HonoAppBuilder` registers procedures of every kind from the same factory and mounts them on one Hono `app`. The optional `app?: Hono` config lets you mount onto an existing Hono instance that already has custom middleware, health checks, or static routes; if omitted, the builder constructs its own.
|
|
238
284
|
|
|
239
285
|
**Key methods:**
|
|
240
286
|
|
|
@@ -248,11 +294,13 @@ All four builders' `build()` methods are synchronous — they return the framewo
|
|
|
248
294
|
| Property | Type | Description |
|
|
249
295
|
|----------|------|-------------|
|
|
250
296
|
| `app` | Framework app | The underlying framework application |
|
|
251
|
-
| `docs` | Route doc array | Route documentation
|
|
297
|
+
| `docs` | Route doc array | Route documentation, lazily computed on first read; `build()` also populates it. |
|
|
252
298
|
|
|
253
299
|
### DocRegistry
|
|
254
300
|
|
|
255
|
-
|
|
301
|
+
For single-builder apps, prefer `builder.toDocEnvelope({ basePath, errors })` — it wraps `DocRegistry` for the common case and produces an envelope identical to the multi-app aggregator.
|
|
302
|
+
|
|
303
|
+
For multi-app aggregation (e.g., two `HonoAppBuilder` instances mounted on different prefixes), `DocRegistry` composes route documentation from every source into one typed envelope:
|
|
256
304
|
|
|
257
305
|
```typescript
|
|
258
306
|
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
@@ -261,9 +309,8 @@ const docs = new DocRegistry({
|
|
|
261
309
|
basePath: '/api',
|
|
262
310
|
errors: appErrors, // your ErrorTaxonomy — framework defaults auto-merged and deduped
|
|
263
311
|
})
|
|
264
|
-
.from(
|
|
265
|
-
.from(
|
|
266
|
-
.from(streamBuilder)
|
|
312
|
+
.from(builderA)
|
|
313
|
+
.from(builderB)
|
|
267
314
|
|
|
268
315
|
app.get('/docs', (c) => c.json(docs.toJSON()))
|
|
269
316
|
```
|
|
@@ -271,34 +318,37 @@ app.get('/docs', (c) => c.json(docs.toJSON()))
|
|
|
271
318
|
- `from()` stores a reference — routes are read lazily at `toJSON()` time
|
|
272
319
|
- `toJSON()` supports optional `filter` and `transform` options
|
|
273
320
|
- `errors` accepts an `ErrorTaxonomy` or raw `ErrorDoc[]`; framework defaults are auto-merged (opt out via `includeDefaults: false`)
|
|
274
|
-
-
|
|
321
|
+
- Every builder satisfies the `DocSource` interface (`{ readonly docs: AnyHttpRouteDoc[] }`)
|
|
275
322
|
|
|
276
323
|
### Client Code Generation
|
|
277
324
|
|
|
278
325
|
Generate type-safe client SDKs from your DocRegistry output. The codegen reads the `DocEnvelope` JSON and produces per-scope TypeScript files with typed params, response types, and callable functions.
|
|
279
326
|
|
|
280
|
-
**Requirements:**
|
|
327
|
+
**Requirements:** every route needs a `scope` field for file grouping. RPC and stream routes carry `scope` via `RPCConfig`. REST routes (`CreateHttp` / `CreateHttpStream`) carry an optional `scope` directly on the procedure config:
|
|
281
328
|
|
|
282
329
|
```typescript
|
|
283
|
-
|
|
284
|
-
|
|
330
|
+
procs.CreateHttp('GetUser', {
|
|
331
|
+
path: '/users/:id',
|
|
332
|
+
method: 'get',
|
|
285
333
|
scope: 'users', // Used for codegen file grouping
|
|
286
|
-
})
|
|
334
|
+
schema: { req: { pathParams: Type.Object({ id: Type.String() }) } },
|
|
335
|
+
}, async (_, { pathParams }) => /* ... */)
|
|
287
336
|
```
|
|
288
337
|
|
|
289
|
-
**Route
|
|
338
|
+
**Route-doc kinds and how the codegen consumes them:**
|
|
290
339
|
|
|
291
|
-
|
|
|
292
|
-
|
|
293
|
-
|
|
|
294
|
-
|
|
|
295
|
-
|
|
|
340
|
+
| Doc kind | Source procedure | Params shape in generated callable | Generated callable |
|
|
341
|
+
|----------|------------------|------------------------------------|--------------------|
|
|
342
|
+
| `rpc` | `Create()` | `jsonSchema.body` → flat params | `client.call()` |
|
|
343
|
+
| `api` | `CreateHttp()` | `jsonSchema.req.{pathParams,query,body,headers}` → structured params | `client.call()` |
|
|
344
|
+
| `stream` | `CreateStream()` | `jsonSchema.params` → flat params | `client.stream()` |
|
|
345
|
+
| `http-stream` | `CreateHttpStream()` | `jsonSchema.req.{pathParams,query,body,headers}` → structured params | `client.stream()` |
|
|
296
346
|
|
|
297
347
|
**SSE return values:** Stream procedures that return a value have it sent as `event: 'return'` SSE message. The client's `TypedStream.result` promise resolves with this value.
|
|
298
348
|
|
|
299
349
|
**SSE yieldType unwrapping:** Codegen detects the SSE envelope in `yieldType` and uses the inner `data` schema for the generated `Yield` type.
|
|
300
350
|
|
|
301
|
-
**Kind discriminant:** All route docs include a `kind` field (`'rpc' | 'api' | 'stream'`) for reliable type narrowing in the codegen pipeline and consumer code.
|
|
351
|
+
**Kind discriminant:** All route docs include a `kind` field (`'rpc' | 'api' | 'stream' | 'http-stream'`) for reliable type narrowing in the codegen pipeline and consumer code.
|
|
302
352
|
|
|
303
353
|
**Namespace types:** Use `--namespace-types` to wrap generated types in nested TS namespaces (`Scope.Route.Params`) instead of flat prefixed exports (`RouteParams`).
|
|
304
354
|
|
|
@@ -308,17 +358,6 @@ apiBuilder.register(factory, (c) => ctx, {
|
|
|
308
358
|
|
|
309
359
|
**Clean output directory:** Use `--clean-out-dir` (or `cleanOutDir: true` in the programmatic API) to recursively remove the output directory before writing, so scope files left over from deleted or renamed scopes are pruned. Skipped under `--dry-run`.
|
|
310
360
|
|
|
311
|
-
## Framework Comparison
|
|
312
|
-
|
|
313
|
-
| Aspect | Express | Hono |
|
|
314
|
-
|--------|---------|------|
|
|
315
|
-
| Context param | `req: express.Request` | `c: Context` |
|
|
316
|
-
| Error handler return | `void` (mutates res) | `Response` |
|
|
317
|
-
| Body access | `req.body` | `await c.req.json()` |
|
|
318
|
-
| Header access | `req.headers['x-id']` | `c.req.header('x-id')` |
|
|
319
|
-
| JSON middleware | Auto-added (or manual) | Built-in |
|
|
320
|
-
| Streaming support | Not yet | `hono-stream` |
|
|
321
|
-
|
|
322
361
|
## TypeScript Types
|
|
323
362
|
|
|
324
363
|
```typescript
|
|
@@ -326,6 +365,7 @@ import type {
|
|
|
326
365
|
RPCConfig,
|
|
327
366
|
RPCHttpRouteDoc,
|
|
328
367
|
StreamHttpRouteDoc,
|
|
368
|
+
HttpStreamRouteDoc,
|
|
329
369
|
StreamMode,
|
|
330
370
|
APIConfig,
|
|
331
371
|
APIHttpRouteDoc,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `ts-procedures/astro`
|
|
2
2
|
|
|
3
|
-
Drop one or more pre-built Hono apps (from `
|
|
3
|
+
Drop one or more pre-built Hono apps (from `HonoAppBuilder`, `HonoAppBuilder`, or `HonoAppBuilder`) into a single Astro catch-all endpoint. Procedure handlers can read Astro's per-request data (`locals`, `cookies`, `redirect`, `params`) inside their factory-context closures.
|
|
4
4
|
|
|
5
5
|
> **Requires SSR.** Your Astro project must use `output: 'server'` or `output: 'hybrid'` with `export const prerender = false` in the catch-all file. Static-prerender mode bypasses live endpoints.
|
|
6
6
|
|
|
@@ -16,12 +16,12 @@ npm install ts-procedures hono astro
|
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
18
|
// src/server/api.ts
|
|
19
|
-
import {
|
|
19
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
20
20
|
import { getAstroContext } from 'ts-procedures/astro'
|
|
21
21
|
import { usersAPI } from './procedures/users'
|
|
22
22
|
import { db } from './db'
|
|
23
23
|
|
|
24
|
-
export const apiApp = new
|
|
24
|
+
export const apiApp = new HonoAppBuilder()
|
|
25
25
|
.register(usersAPI, (c) => {
|
|
26
26
|
const astro = getAstroContext(c)
|
|
27
27
|
return { db, user: astro.locals.user ?? null }
|
|
@@ -81,9 +81,8 @@ Dispatch rules:
|
|
|
81
81
|
|
|
82
82
|
## Streams
|
|
83
83
|
|
|
84
|
-
`
|
|
84
|
+
`HonoAppBuilder` returns a `Response` with a `ReadableStream` body. Astro SSR forwards that body verbatim — no additional configuration. Client disconnects abort `ctx.signal` inside the stream handler.
|
|
85
85
|
|
|
86
86
|
## What's NOT included
|
|
87
87
|
|
|
88
|
-
- Express RPC support (Express uses Node `req`/`res`, not Web Fetch).
|
|
89
88
|
- DocRegistry coupling — wire `DocRegistry` against the same builders separately for client codegen.
|