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/README.md
CHANGED
|
@@ -48,13 +48,75 @@ const user = await GetUser({}, { userId: '123' })
|
|
|
48
48
|
const user2 = await procedure({}, { userId: '456' })
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## v8 Migration — Unified `HonoAppBuilder`
|
|
52
|
+
|
|
53
|
+
The three Hono builders (`HonoRPCAppBuilder`, `HonoAPIAppBuilder`, `HonoStreamAppBuilder`) are replaced in v8 by a single `HonoAppBuilder` that accepts any `Procedures<>()` factory and dispatches all four procedure kinds (`rpc`, `rpc-stream`, `http`, `http-stream`) by `procedure.kind` from a single `register()` call.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Before (v7)
|
|
57
|
+
const rpc = new HonoRPCAppBuilder({ app, errors }).register(F1, ctx1)
|
|
58
|
+
const api = new HonoAPIAppBuilder({ app, errors, queryParser }).register(F2, ctx2)
|
|
59
|
+
const stream = new HonoStreamAppBuilder({ app, errors, onMidStreamError }).register(F3, ctx3)
|
|
60
|
+
|
|
61
|
+
// After (v8)
|
|
62
|
+
const builder = new HonoAppBuilder({
|
|
63
|
+
app,
|
|
64
|
+
errors,
|
|
65
|
+
api: { queryParser },
|
|
66
|
+
stream: { onMidStreamError },
|
|
67
|
+
}).register(F1, ctx1).register(F2, ctx2).register(F3, ctx3)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Kind-specific config knobs are stratified into `rpc.*`, `api.*`, and `stream.*` blocks. Single-app users can call `builder.toDocEnvelope()` directly (no `DocRegistry` needed). `DocRegistry` remains for multi-app aggregation.
|
|
71
|
+
|
|
72
|
+
Subpath imports change from `ts-procedures/hono-rpc`, `ts-procedures/hono-api`, `ts-procedures/hono-stream` to `ts-procedures/hono`.
|
|
73
|
+
|
|
74
|
+
## HTTP Routes (v8) — `CreateHttp`
|
|
75
|
+
|
|
76
|
+
v8 introduces `CreateHttp` and `CreateHttpStream` as first-class HTTP creators. HTTP fields (`path`, `method`, `scope`, `errors`) live on the creator config directly; no factory type parameter is required.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { Procedures } from 'ts-procedures'
|
|
80
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
81
|
+
import { Type } from 'typebox'
|
|
82
|
+
|
|
83
|
+
type AppContext = { userId: string }
|
|
84
|
+
const API = Procedures<AppContext>()
|
|
85
|
+
|
|
86
|
+
// Input channels: pathParams, query, body, headers (schema.req)
|
|
87
|
+
// Response: schema.res.body (docs) + schema.res.headers (makes handler return { body, headers })
|
|
88
|
+
API.CreateHttp('GetUser', {
|
|
89
|
+
path: '/users/:id',
|
|
90
|
+
method: 'get',
|
|
91
|
+
schema: {
|
|
92
|
+
req: { pathParams: Type.Object({ id: Type.String() }) },
|
|
93
|
+
res: { body: Type.Object({ id: Type.String(), name: Type.String() }) },
|
|
94
|
+
},
|
|
95
|
+
}, async (ctx, { pathParams }) => fetchUser(pathParams.id))
|
|
96
|
+
|
|
97
|
+
API.CreateHttp('CreateUser', {
|
|
98
|
+
path: '/users',
|
|
99
|
+
method: 'post',
|
|
100
|
+
schema: {
|
|
101
|
+
req: { body: Type.Object({ name: Type.String(), email: Type.String() }) },
|
|
102
|
+
},
|
|
103
|
+
}, async (ctx, { body }) => createUser(body))
|
|
104
|
+
|
|
105
|
+
const app = new HonoAppBuilder({ pathPrefix: '/api' })
|
|
106
|
+
.register(API, (c) => ({ userId: c.req.header('x-user-id') || 'anonymous' }))
|
|
107
|
+
.build()
|
|
108
|
+
// GET /api/users/:id → 200, POST /api/users → 201
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Migrating from v7?** Replace `Procedures<Ctx, APIConfig>()` → `Procedures<Ctx>()`, `.Create(` → `.CreateHttp(`, and `schema.input` → `schema.req`. Then re-run `npx ts-procedures-codegen`. See `CHANGELOG.md` for the full breaking-changes list.
|
|
112
|
+
|
|
51
113
|
## Features
|
|
52
114
|
|
|
53
|
-
- **[Core Procedures](docs/core.md)** — Type-safe procedure definitions with `Procedures()`, `Create`, and `CreateStream`. Includes schema validation with TypeBox, error handling, generics, testing patterns, and the full API reference.
|
|
115
|
+
- **[Core Procedures](docs/core.md)** — Type-safe procedure definitions with `Procedures()`, `Create`, `CreateHttp`, and `CreateStream`. Includes schema validation with TypeBox, error handling, generics, testing patterns, and the full API reference.
|
|
54
116
|
|
|
55
|
-
- **[Streaming](docs/streaming.md)** — Async generator procedures with yield validation, abort signal integration, SSE examples, and stream error handling.
|
|
117
|
+
- **[Streaming](docs/streaming.md)** — Async generator procedures with yield validation, abort signal integration, SSE examples, and stream error handling. `CreateHttpStream` supports response headers via `TypedStream.headers`.
|
|
56
118
|
|
|
57
|
-
- **[HTTP Integrations](docs/http-integrations.md)** —
|
|
119
|
+
- **[HTTP Integrations](docs/http-integrations.md)** — `HonoAppBuilder` with lifecycle hooks, route documentation, `DocRegistry` for composing docs, and per-channel input validation via `schema.req`.
|
|
58
120
|
|
|
59
121
|
- **[Client Code Generation](docs/client-and-codegen.md)** — Generate type-safe client SDKs from your server's `DocRegistry`. CLI and programmatic API, adapters, hooks, streaming support, and self-contained mode.
|
|
60
122
|
|
|
@@ -22,7 +22,7 @@ When asked to plan an API or procedure set:
|
|
|
22
22
|
3. **List procedures** — name, type (Create vs CreateStream), description
|
|
23
23
|
4. **Design schemas** — params (validated), returnType/yieldType (documented)
|
|
24
24
|
5. **Design context** — what each handler needs from the request
|
|
25
|
-
6. **Choose HTTP implementation** —
|
|
25
|
+
6. **Choose HTTP implementation** — `HonoAppBuilder` covers all four procedure kinds (rpc, rpc-stream, http, http-stream); pick which kinds the app needs
|
|
26
26
|
7. **Plan error handling** — which layer for each error type
|
|
27
27
|
8. **Map route structure** — scope + version for RPC routes, path + method for API routes
|
|
28
28
|
|
|
@@ -109,19 +109,17 @@ type AuthContext = { userId: string; requestId: string; db: Database }
|
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
### HTTP Setup
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
- HonoAPIAppBuilder for REST-style endpoints
|
|
115
|
-
- DocRegistry to compose docs from all builders
|
|
112
|
+
- HonoAppBuilder for RPC, streams (SSE/text), and REST endpoints
|
|
113
|
+
- DocRegistry to compose docs across multiple builders (or `builder.toDocEnvelope()` for single-app)
|
|
116
114
|
- Path prefix: /api
|
|
117
115
|
|
|
118
116
|
### Route Map
|
|
119
117
|
- POST /api/health/health-check/1
|
|
120
118
|
- POST /api/users/get-user/1
|
|
121
119
|
- GET|POST /api/activity/stream-activity/1
|
|
122
|
-
- GET /api/users/:id (
|
|
123
|
-
- POST /api/users (
|
|
124
|
-
- DELETE /api/users/:id (
|
|
120
|
+
- GET /api/users/:id (HonoAppBuilder)
|
|
121
|
+
- POST /api/users (HonoAppBuilder, 201)
|
|
122
|
+
- DELETE /api/users/:id (HonoAppBuilder, 204)
|
|
125
123
|
|
|
126
124
|
### Error Handling (pick a mode, optionally combine)
|
|
127
125
|
- **Declarative mode (recommended for structured apps)**: `defineErrorTaxonomy({ AuthError: {class, 401}, NotFoundError: {class, 404}, ... })` wired via `errors` config. Drives typed client dispatch + DocEnvelope.
|
|
@@ -66,7 +66,7 @@ Uses **TypeBox** for schema definitions (`import { Type } from 'typebox'`):
|
|
|
66
66
|
| `schema.params` | **Validated at runtime** via AJV |
|
|
67
67
|
| `schema.returnType` | **Documentation only**, never validated |
|
|
68
68
|
| `schema.yieldType` | Validated only if `validateYields: true` in CreateStream config |
|
|
69
|
-
| `schema.
|
|
69
|
+
| `schema.req` | Structured multi-channel HTTP input (`pathParams`, `query`, `body`, `headers`) — available on `CreateHttp` / `CreateHttpStream` only |
|
|
70
70
|
|
|
71
71
|
AJV config: `allErrors: true`, `coerceTypes: true`, `removeAdditional: true`
|
|
72
72
|
|
|
@@ -94,7 +94,7 @@ const appErrors = defineErrorTaxonomy({
|
|
|
94
94
|
UseCaseError: { class: UseCaseError, statusCode: 422, toResponse: (e) => ({ message: e.externalMsg }) },
|
|
95
95
|
})
|
|
96
96
|
|
|
97
|
-
new
|
|
97
|
+
new HonoAppBuilder({ errors: appErrors, unknownError: { toResponse: () => ({ error: '...' }) } })
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
Per-route narrowing: `APIConfig<keyof typeof appErrors & string>` / `RPCConfig<keyof typeof appErrors & string>` with a `errors: [...]` array on the config. Generated `_errors.ts` emits runtime classes — clients catch with `instanceof ${Service}Errors.${Name}` when using `create${Service}Client`.
|
|
@@ -103,13 +103,10 @@ Full contract: `docs/http-integrations.md § Error Handling` (canonical) and `ap
|
|
|
103
103
|
|
|
104
104
|
## HTTP Implementations
|
|
105
105
|
|
|
106
|
-
| Builder | Import |
|
|
107
|
-
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `HonoStreamAppBuilder` | `ts-procedures/hono-stream` | SSE or newline-delimited JSON |
|
|
111
|
-
| `HonoAPIAppBuilder` | `ts-procedures/hono-api` | REST-style with per-channel input |
|
|
112
|
-
| `DocRegistry` | `ts-procedures/http-docs` | Compose route docs from multiple builders |
|
|
106
|
+
| Builder | Import | Handles |
|
|
107
|
+
|---------|--------|---------|
|
|
108
|
+
| `HonoAppBuilder` | `ts-procedures/hono` | RPC, REST-style HTTP, and streaming (SSE / newline-JSON) — one builder dispatches all four procedure kinds |
|
|
109
|
+
| `DocRegistry` | `ts-procedures/http-docs` | Compose route docs from multiple builders (single-app users can also call `builder.toDocEnvelope()`) |
|
|
113
110
|
|
|
114
111
|
### Route Path Format (RPC)
|
|
115
112
|
|
|
@@ -122,24 +119,28 @@ Example: `Create('GetUser', { scope: 'users', version: 1 }, ...)` --> `POST /use
|
|
|
122
119
|
### Builder Pattern
|
|
123
120
|
|
|
124
121
|
```typescript
|
|
125
|
-
const app = new
|
|
126
|
-
.register(factory, (
|
|
122
|
+
const app = new HonoAppBuilder({ pathPrefix: '/api' })
|
|
123
|
+
.register(factory, (c) => ({ /* context */ }))
|
|
127
124
|
.build()
|
|
128
125
|
```
|
|
129
126
|
|
|
130
127
|
### Lifecycle Hooks (HTTP builders)
|
|
131
128
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
|
135
|
-
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
129
|
+
For `HonoAppBuilder` (v8), hooks are stratified: cross-cutting hooks live at the top level, kind-specific hooks live inside `rpc:` / `api:` / `stream:` blocks.
|
|
130
|
+
|
|
131
|
+
| Hook | Block | When |
|
|
132
|
+
|------|-------|------|
|
|
133
|
+
| `onRequestStart` | top level | Before context resolution (every kind) |
|
|
134
|
+
| `onRequestEnd` | top level | After response sent (every kind) |
|
|
135
|
+
| `onError` | top level | Imperative pre-stream / unary error callback — first-class peer of the declarative `errors` taxonomy |
|
|
136
|
+
| `onRequestError` | top level | Cross-cutting observer — fires for every caught error before dispatch |
|
|
137
|
+
| `rpc.onSuccess` | `rpc` block | After successful `Create` handler execution |
|
|
138
|
+
| `api.onSuccess` | `api` block | After successful `CreateHttp` handler execution |
|
|
139
|
+
| `api.queryParser` | `api` block | Custom query string parser for `CreateHttp` routes |
|
|
140
|
+
| `stream.defaultStreamMode` | `stream` block | Default mode for streaming routes (`'sse'` or `'text'`) |
|
|
141
|
+
| `stream.onStreamStart` | `stream` block | Before first yield |
|
|
142
|
+
| `stream.onStreamEnd` | `stream` block | After stream closes |
|
|
143
|
+
| `stream.onMidStreamError` | `stream` block | Error during streaming (return data to yield as final event — the only mid-stream path) |
|
|
143
144
|
|
|
144
145
|
## Decision Framework
|
|
145
146
|
|
|
@@ -148,17 +149,13 @@ const app = new ExpressRPCAppBuilder({ pathPrefix: '/api' })
|
|
|
148
149
|
- Request --> multiple values over time --> **CreateStream**
|
|
149
150
|
|
|
150
151
|
**Which schema approach?**
|
|
151
|
-
- RPC-style (single `params` object, POST-only) --> `schema.params`
|
|
152
|
-
- REST-style (multiple HTTP input sources) --> `schema.
|
|
153
|
-
- `schema.params` and `schema.input` are **mutually exclusive**
|
|
152
|
+
- RPC-style (single `params` object, POST-only) --> `Create` / `CreateStream` with `schema.params`
|
|
153
|
+
- REST-style (multiple HTTP input sources: pathParams, query, body, headers) --> `CreateHttp` / `CreateHttpStream` with `schema.req`
|
|
154
154
|
|
|
155
155
|
**Which HTTP implementation?**
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
- Hono app (REST-style) --> **HonoAPIAppBuilder**
|
|
160
|
-
- SSE with browser EventSource --> `streamMode: 'sse'`
|
|
161
|
-
- Simple text streaming --> `streamMode: 'text'`
|
|
156
|
+
- Hono app (any combination of RPC / REST / streaming) --> **HonoAppBuilder** (one builder dispatches every kind)
|
|
157
|
+
- SSE with browser EventSource --> `stream.defaultStreamMode: 'sse'` on the builder, or `streamMode: 'sse'` per `register()` call
|
|
158
|
+
- Simple text streaming --> `stream.defaultStreamMode: 'text'`
|
|
162
159
|
|
|
163
160
|
**How to group procedures?**
|
|
164
161
|
- By domain: `UserProcedures`, `OrderProcedures`
|
|
@@ -171,9 +168,9 @@ The npm package ships user-facing documentation with narrative explanations and
|
|
|
171
168
|
|
|
172
169
|
| File | Covers |
|
|
173
170
|
|------|--------|
|
|
174
|
-
| `docs/core.md` | Procedures factory, Create, CreateStream, schema.
|
|
171
|
+
| `docs/core.md` | Procedures factory, Create, CreateStream, schema.req, error handling |
|
|
175
172
|
| `docs/streaming.md` | Streaming procedures, AbortSignal, SSE patterns |
|
|
176
|
-
| `docs/http-integrations.md` |
|
|
173
|
+
| `docs/http-integrations.md` | Hono unified builder (`HonoAppBuilder` — RPC + HTTP + streaming with stratified config), **error taxonomy (canonical)**, DocRegistry (unified constructor, `.documentError()`) |
|
|
177
174
|
| `docs/client-and-codegen.md` | Client code generation, `createApiClient`/`createClient`, typed error dispatch, per-route `Errors` unions, per-call options, client-level defaults, typed RequestMeta augmentation, CLI options |
|
|
178
175
|
| `docs/client-error-handling.md` | **Canonical guide** for 7.0+ client error surface: normalized error classes, `.safe()` Result API, `ClientErrorMap` augmentation, custom `ErrorClassifier`, migration from `ClientRequestError`. |
|
|
179
176
|
| `CHANGELOG.md` | Release notes — see `[7.0.0]` for the safe-result API, new error classes, and `ClientRequestError` → `ClientHttpError` rename. See `[6.0.0]` for the peer error-handling model (taxonomy + `onError` + `onRequestError`). |
|
|
@@ -342,29 +342,37 @@ Create('GetUser', {}, async (ctx, params) => {
|
|
|
342
342
|
|
|
343
343
|
---
|
|
344
344
|
|
|
345
|
-
## 12.
|
|
345
|
+
## 12. Splitting Procedures Across Multiple HonoAppBuilder Instances
|
|
346
346
|
|
|
347
|
-
**Problem:**
|
|
347
|
+
**Problem:** Creating one `HonoAppBuilder` per procedure kind (RPC, stream, HTTP) the way v7 required — duplicating cross-cutting config (errors, lifecycle hooks, context factories) in every instance.
|
|
348
348
|
|
|
349
349
|
```typescript
|
|
350
|
-
// BAD —
|
|
351
|
-
const {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
new HonoStreamAppBuilder()
|
|
356
|
-
.register(factory, ctx) // Only StreamEvents is registered
|
|
357
|
-
.build()
|
|
350
|
+
// BAD — three builders, three copies of the same config
|
|
351
|
+
const rpcApp = new HonoAppBuilder({ errors: appErrors }).register(RPC, ctx).build()
|
|
352
|
+
const streamApp = new HonoAppBuilder({ errors: appErrors }).register(StreamRPC, ctx).build()
|
|
353
|
+
const apiApp = new HonoAppBuilder({ errors: appErrors }).register(API, ctx).build()
|
|
358
354
|
```
|
|
359
355
|
|
|
360
|
-
**Fix:**
|
|
356
|
+
**Fix:** In v8, `HonoAppBuilder` dispatches all four procedure kinds (`Create`, `CreateStream`, `CreateHttp`, `CreateHttpStream`) from a single `register()` call. One builder, one config block, all procedures.
|
|
361
357
|
|
|
362
358
|
```typescript
|
|
363
|
-
// GOOD
|
|
364
|
-
const
|
|
365
|
-
|
|
359
|
+
// GOOD — one builder, all procedure kinds
|
|
360
|
+
const procs = Procedures<AppContext, RPCConfig>()
|
|
361
|
+
procs.Create('GetUser', { scope: 'users', version: 1 }, rpcHandler)
|
|
362
|
+
procs.CreateStream('StreamEvents', { scope: 'events', version: 1 }, streamHandler)
|
|
363
|
+
procs.CreateHttp('CreateUser', { path: '/users', method: 'post', schema: { /* ... */ } }, httpHandler)
|
|
364
|
+
|
|
365
|
+
const app = new HonoAppBuilder({
|
|
366
|
+
errors: appErrors,
|
|
367
|
+
unknownError: { toResponse: (err) => ({ error: (err as Error).message }) },
|
|
368
|
+
stream: { defaultStreamMode: 'sse' },
|
|
369
|
+
})
|
|
370
|
+
.register(procs, (c) => ({ /* ctx */ }))
|
|
371
|
+
.build()
|
|
366
372
|
```
|
|
367
373
|
|
|
374
|
+
**Why:** v8 collapsed v7's `HonoRPCAppBuilder` / `HonoStreamAppBuilder` / `HonoAPIAppBuilder` into one builder with stratified config (`rpc.{onSuccess}`, `api.{queryParser, onSuccess}`, `stream.{defaultStreamMode, onStreamStart, onStreamEnd, onMidStreamError}`). Cross-cutting config (`errors`, `unknownError`, `onError`, `onRequestError`, `onRequestStart`, `onRequestEnd`) lives at the top level and applies uniformly across kinds.
|
|
375
|
+
|
|
368
376
|
---
|
|
369
377
|
|
|
370
378
|
## 13. Missing Schema at Registration Time
|
|
@@ -398,9 +406,14 @@ Create('GetUser', {
|
|
|
398
406
|
**Problem:** Using a single error handler for both validation errors (before streaming) and runtime errors (during streaming).
|
|
399
407
|
|
|
400
408
|
```typescript
|
|
401
|
-
// BAD — onError
|
|
402
|
-
|
|
403
|
-
|
|
409
|
+
// BAD — top-level onError fires for pre-stream errors only.
|
|
410
|
+
// Errors thrown after the first yield will NOT reach this callback.
|
|
411
|
+
new HonoAppBuilder({
|
|
412
|
+
onError: (proc, c, err) => {
|
|
413
|
+
// Catches pre-stream (validation / context) errors only.
|
|
414
|
+
// Mid-stream errors silently pass through — the HTTP status is already
|
|
415
|
+
// committed, so the response can't be rewritten here.
|
|
416
|
+
},
|
|
404
417
|
})
|
|
405
418
|
```
|
|
406
419
|
|
|
@@ -410,15 +423,17 @@ new HonoStreamAppBuilder({
|
|
|
410
423
|
// GOOD
|
|
411
424
|
import { defineErrorTaxonomy } from 'ts-procedures/http-errors'
|
|
412
425
|
|
|
413
|
-
new
|
|
426
|
+
new HonoAppBuilder({
|
|
414
427
|
// Taxonomy handles all pre-stream errors (validation, auth, context)
|
|
415
428
|
errors: defineErrorTaxonomy({
|
|
416
429
|
AuthError: { class: AuthError, statusCode: 401 },
|
|
417
430
|
}),
|
|
418
431
|
unknownError: { toResponse: (err) => ({ error: (err as Error).message }) },
|
|
419
|
-
// Mid-stream still goes through onMidStreamError — the HTTP status
|
|
420
|
-
// already committed, so errors become yields, not responses.
|
|
421
|
-
|
|
432
|
+
// Mid-stream still goes through stream.onMidStreamError — the HTTP status
|
|
433
|
+
// is already committed, so errors become yields, not responses.
|
|
434
|
+
stream: {
|
|
435
|
+
onMidStreamError: (proc, c, error) => ({ data: { error: error.message }, closeStream: true }),
|
|
436
|
+
},
|
|
422
437
|
})
|
|
423
438
|
```
|
|
424
439
|
|
|
@@ -516,75 +531,70 @@ const appErrors = defineErrorTaxonomy({
|
|
|
516
531
|
},
|
|
517
532
|
})
|
|
518
533
|
|
|
519
|
-
const app = new
|
|
534
|
+
const app = new HonoAppBuilder({
|
|
520
535
|
errors: appErrors,
|
|
521
536
|
unknownError: { toResponse: () => ({ error: 'Internal server error' }) },
|
|
522
537
|
})
|
|
523
|
-
.register(factory, async (
|
|
524
|
-
const user = await authenticate(req.
|
|
538
|
+
.register(factory, async (c) => {
|
|
539
|
+
const user = await authenticate(c.req.header('authorization'))
|
|
525
540
|
if (!user) throw new AuthError('invalid token')
|
|
526
541
|
return { userId: user.id }
|
|
527
542
|
})
|
|
528
543
|
```
|
|
529
544
|
|
|
530
|
-
The imperative `onError: (procedure,
|
|
545
|
+
The imperative `onError: (procedure, c, error) => Response` callback is a first-class peer — use it for apps that prefer a single response handler or when you want to handle the tail of errors the taxonomy doesn't cover.
|
|
531
546
|
|
|
532
547
|
---
|
|
533
548
|
|
|
534
|
-
## 18.
|
|
549
|
+
## 18. Mixing schema.params and schema.req on the same procedure
|
|
535
550
|
|
|
536
|
-
**Problem:**
|
|
551
|
+
**Problem:** `schema.params` (RPC channel) and `schema.req` (HTTP channel) are mutually exclusive on `CreateHttp`. Using both throws at registration.
|
|
537
552
|
|
|
538
553
|
```typescript
|
|
539
554
|
// BAD — throws ProcedureRegistrationError at registration
|
|
540
|
-
|
|
555
|
+
API.CreateHttp('GetUser', {
|
|
541
556
|
path: '/users/:id',
|
|
542
557
|
method: 'get',
|
|
543
558
|
schema: {
|
|
544
|
-
params: Type.Object({ id: Type.String() }),
|
|
545
|
-
|
|
559
|
+
params: Type.Object({ id: Type.String() }), // RPC-only field — invalid on CreateHttp
|
|
560
|
+
req: {
|
|
546
561
|
pathParams: Type.Object({ id: Type.String() }),
|
|
547
562
|
},
|
|
548
563
|
},
|
|
549
564
|
}, handler)
|
|
550
565
|
```
|
|
551
566
|
|
|
552
|
-
**Fix:**
|
|
567
|
+
**Fix:** Use `schema.req` for HTTP routes (`CreateHttp`). Use `schema.params` for RPC routes (`Create`). Never mix them.
|
|
553
568
|
|
|
554
569
|
```typescript
|
|
555
|
-
// GOOD — schema.
|
|
556
|
-
|
|
570
|
+
// GOOD — CreateHttp with schema.req for per-channel validation
|
|
571
|
+
API.CreateHttp('GetUser', {
|
|
557
572
|
path: '/users/:id',
|
|
558
573
|
method: 'get',
|
|
559
574
|
schema: {
|
|
560
|
-
|
|
561
|
-
pathParams: Type.Object({ id: Type.String() }),
|
|
562
|
-
},
|
|
575
|
+
req: { pathParams: Type.Object({ id: Type.String() }) },
|
|
563
576
|
},
|
|
564
577
|
}, async (ctx, { pathParams }) => fetchUser(pathParams.id))
|
|
565
578
|
|
|
566
|
-
// GOOD — schema.params for
|
|
567
|
-
Create('GetUser', {
|
|
568
|
-
scope: 'users', version: 1,
|
|
579
|
+
// GOOD — Create with schema.params for flat RPC-style
|
|
580
|
+
const { GetUser } = Create('GetUser', {
|
|
569
581
|
schema: { params: Type.Object({ id: Type.String() }) },
|
|
570
582
|
}, async (ctx, params) => fetchUser(params.id))
|
|
571
583
|
```
|
|
572
584
|
|
|
573
|
-
**Why:** `schema.params` and `schema.input` are mutually exclusive by design. They represent different input paradigms — flat (RPC) vs structured (HTTP API).
|
|
574
|
-
|
|
575
585
|
---
|
|
576
586
|
|
|
577
|
-
## 19. Mismatched Path Param Names in schema.
|
|
587
|
+
## 19. Mismatched Path Param Names in schema.req.pathParams
|
|
578
588
|
|
|
579
|
-
**Problem:** Path template param names don't match `schema.
|
|
589
|
+
**Problem:** Path template param names don't match `schema.req.pathParams` property names.
|
|
580
590
|
|
|
581
591
|
```typescript
|
|
582
|
-
// BAD — path has :id but schema declares userId — throws at
|
|
583
|
-
|
|
592
|
+
// BAD — path has :id but schema declares userId — throws at registration
|
|
593
|
+
API.CreateHttp('GetUser', {
|
|
584
594
|
path: '/users/:id',
|
|
585
595
|
method: 'get',
|
|
586
596
|
schema: {
|
|
587
|
-
|
|
597
|
+
req: {
|
|
588
598
|
pathParams: Type.Object({ userId: Type.String() }), // Wrong name!
|
|
589
599
|
},
|
|
590
600
|
},
|
|
@@ -595,18 +605,18 @@ Create('GetUser', {
|
|
|
595
605
|
|
|
596
606
|
```typescript
|
|
597
607
|
// GOOD
|
|
598
|
-
|
|
608
|
+
API.CreateHttp('GetUser', {
|
|
599
609
|
path: '/users/:id',
|
|
600
610
|
method: 'get',
|
|
601
611
|
schema: {
|
|
602
|
-
|
|
612
|
+
req: {
|
|
603
613
|
pathParams: Type.Object({ id: Type.String() }), // Matches :id
|
|
604
614
|
},
|
|
605
615
|
},
|
|
606
616
|
}, handler)
|
|
607
617
|
```
|
|
608
618
|
|
|
609
|
-
**Why:** `
|
|
619
|
+
**Why:** `CreateHttp` validates at registration time that `:param` names in the path match `schema.req.pathParams` property names. A mismatch throws `ProcedureRegistrationError` immediately so you catch it at startup, not at runtime.
|
|
610
620
|
|
|
611
621
|
---
|
|
612
622
|
|
|
@@ -616,7 +626,7 @@ Create('GetUser', {
|
|
|
616
626
|
|
|
617
627
|
```typescript
|
|
618
628
|
// BAD — duplicated across builders, invisible to generated clients
|
|
619
|
-
new
|
|
629
|
+
new HonoAppBuilder({
|
|
620
630
|
onError: (procedure, c, error) => {
|
|
621
631
|
if (error instanceof ValidationError) return c.json({ error: error.message }, 400)
|
|
622
632
|
if (error instanceof AuthError) return c.json({ error: 'Unauthorized' }, 401)
|
|
@@ -642,7 +652,7 @@ const appErrors = defineErrorTaxonomy({
|
|
|
642
652
|
},
|
|
643
653
|
})
|
|
644
654
|
|
|
645
|
-
new
|
|
655
|
+
new HonoAppBuilder({
|
|
646
656
|
errors: appErrors,
|
|
647
657
|
unknownError: {
|
|
648
658
|
toResponse: () => ({ error: 'Internal server error' }),
|
|
@@ -651,7 +661,7 @@ new HonoAPIAppBuilder({
|
|
|
651
661
|
})
|
|
652
662
|
```
|
|
653
663
|
|
|
654
|
-
The same `errors` / `unknownError` shape plugs into
|
|
664
|
+
The same `errors` / `unknownError` shape plugs into `HonoAppBuilder` and applies uniformly across RPC, HTTP, and pre-stream paths. `ctx.error()` still works and is caught by the default taxonomy at 500.
|
|
655
665
|
|
|
656
666
|
**The anti-pattern is the ladder, not `onError`.** `onError` is a first-class peer of the taxonomy — it's perfectly fine for simple apps or when you want full response control. The problem is writing `instanceof` ladders *inside* it to route by error class, because that's exactly what the taxonomy is designed to express declaratively.
|
|
657
667
|
|
|
@@ -729,6 +739,46 @@ const { Create: CreateInternal } = Procedures({
|
|
|
729
739
|
|
|
730
740
|
---
|
|
731
741
|
|
|
742
|
+
## 23. Using `Create` for HTTP routes (v8+)
|
|
743
|
+
|
|
744
|
+
**Problem:** Using `Create` with HTTP-specific fields (`path`, `method`, `schema.input`) — this throws at registration time in v8.
|
|
745
|
+
|
|
746
|
+
```typescript
|
|
747
|
+
// BAD — throws ProcedureRegistrationError in v8; Create is the RPC primitive only
|
|
748
|
+
const API = Procedures<Ctx, APIConfig>()
|
|
749
|
+
|
|
750
|
+
API.Create('GetUser', {
|
|
751
|
+
path: '/users/:id',
|
|
752
|
+
method: 'get',
|
|
753
|
+
schema: { input: { pathParams: Type.Object({ id: Type.String() }) } },
|
|
754
|
+
}, async (ctx, params) => { /* ... */ })
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
**Fix:** Use `CreateHttp` for HTTP routes. `schema.input` is renamed `schema.req` in v8.
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
// GOOD — v8 HTTP surface
|
|
761
|
+
const API = Procedures<Ctx>()
|
|
762
|
+
|
|
763
|
+
API.CreateHttp('GetUser', {
|
|
764
|
+
path: '/users/:id',
|
|
765
|
+
method: 'get',
|
|
766
|
+
schema: { req: { pathParams: Type.Object({ id: Type.String() }) } },
|
|
767
|
+
}, async (ctx, { pathParams }) => fetchUser(pathParams.id))
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
**Migration checklist:**
|
|
771
|
+
|
|
772
|
+
1. Replace `Procedures<Ctx, APIConfig>()` with `Procedures<Ctx>()` for HTTP factories.
|
|
773
|
+
2. Replace `.Create(` with `.CreateHttp(` for every HTTP route.
|
|
774
|
+
3. Rename `schema.input` → `schema.req` (same channels: `pathParams`, `query`, `body`, `headers`).
|
|
775
|
+
4. Move response type to `schema.res.body` (optional — `returnType` was docs-only anyway).
|
|
776
|
+
5. Re-run `npx ts-procedures-codegen` to regenerate clients.
|
|
777
|
+
|
|
778
|
+
**Why:** `Create` is the RPC primitive (`params` / `returnType` only). HTTP routes with `path`, `method`, and per-channel input belong on `CreateHttp` (unary) or `CreateHttpStream` (streaming). Separating the surfaces makes the builder routing explicit and allows `CreateHttp` to support response headers (`schema.res.headers`) without leaking HTTP concepts into the RPC path.
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
732
782
|
## Summary Table
|
|
733
783
|
|
|
734
784
|
| # | Anti-Pattern | Risk | Severity |
|
|
@@ -744,7 +794,7 @@ const { Create: CreateInternal } = Procedures({
|
|
|
744
794
|
| 9 | Manual type coercion | Unnecessary code, coercion mismatch | SUGGESTION |
|
|
745
795
|
| 10 | Expecting extra fields to survive | Silent data loss | WARNING |
|
|
746
796
|
| 11 | Missing context type | No type safety in handlers | WARNING |
|
|
747
|
-
| 12 |
|
|
797
|
+
| 12 | Splitting procedures across multiple HonoAppBuilder instances | Duplicated cross-cutting config; one builder dispatches all kinds in v8 | WARNING |
|
|
748
798
|
| 13 | Plain JSON Schema objects instead of TypeBox | ProcedureRegistrationError | CRITICAL |
|
|
749
799
|
| 14 | Wrong error handler for streams | Unhandled errors or wrong response format | WARNING |
|
|
750
800
|
| 15 | Manual doc building instead of extendProcedureDoc | Fragile, incomplete documentation | SUGGESTION |
|
|
@@ -755,3 +805,4 @@ const { Create: CreateInternal } = Procedures({
|
|
|
755
805
|
| 20 | Hand-writing onError instanceof ladders | Drifting response shapes, untyped client errors | WARNING |
|
|
756
806
|
| 21 | Catching raw DOMException/TypeError from generated callables | Framework normalizes these; raw platform errors won't reach catch blocks after 7.0.0 | WARNING |
|
|
757
807
|
| 22 | `noRuntimeValidation` on a public-facing factory | Untrusted wire data reaches handlers without AJV; coerceTypes/removeAdditional also disabled | CRITICAL |
|
|
808
|
+
| 23 | Using `Create` for HTTP routes (v8+) | ProcedureRegistrationError at startup; HTTP fields require `CreateHttp` / `CreateHttpStream` | CRITICAL |
|