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
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Hono Template: {{Name}}
|
|
2
|
+
|
|
3
|
+
A single `HonoAppBuilder` can host all four procedure kinds — RPC (`Create`), RPC stream (`CreateStream`), HTTP REST (`CreateHttp`), and HTTP stream (`CreateHttpStream`) — registered against one `Procedures` factory and mounted with one `register(...)` call. This template demonstrates that.
|
|
4
|
+
|
|
5
|
+
## Implementation — `{{Name}}.hono.ts`
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { Procedures } from 'ts-procedures'
|
|
9
|
+
import type { RPCConfig } from 'ts-procedures/http'
|
|
10
|
+
import { defineErrorTaxonomy, HonoAppBuilder } from 'ts-procedures/hono'
|
|
11
|
+
import { Type } from 'typebox'
|
|
12
|
+
|
|
13
|
+
// ─── Context ──────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
type {{Name}}Context = {
|
|
16
|
+
userId: string
|
|
17
|
+
requestId: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ─── Procedures ───────────────────────────────────────────
|
|
21
|
+
// Capture the factory in a single `procs` variable so every kind shares
|
|
22
|
+
// the same context type and the same final register() call.
|
|
23
|
+
|
|
24
|
+
const procs = Procedures<{{Name}}Context, RPCConfig>()
|
|
25
|
+
|
|
26
|
+
// RPC — `Create`. Mounted at POST /api/{{kebab}}/get-item/1
|
|
27
|
+
export const { GetItem } = procs.Create(
|
|
28
|
+
'GetItem',
|
|
29
|
+
{
|
|
30
|
+
scope: '{{kebab}}',
|
|
31
|
+
version: 1,
|
|
32
|
+
description: 'Fetch an item by id (RPC).',
|
|
33
|
+
schema: {
|
|
34
|
+
params: Type.Object({
|
|
35
|
+
id: Type.String(),
|
|
36
|
+
}),
|
|
37
|
+
returnType: Type.Object({
|
|
38
|
+
id: Type.String(),
|
|
39
|
+
name: Type.String(),
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
async (ctx, params) => {
|
|
44
|
+
// TODO: implement
|
|
45
|
+
return { id: params.id, name: 'Example' }
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
// RPC stream — `CreateStream`. Mounted at POST /api/{{kebab}}/stream-events/1 (SSE)
|
|
50
|
+
export const { StreamEvents } = procs.CreateStream(
|
|
51
|
+
'StreamEvents',
|
|
52
|
+
{
|
|
53
|
+
scope: '{{kebab}}',
|
|
54
|
+
version: 1,
|
|
55
|
+
description: 'Stream events for the given subject (rpc-stream / SSE).',
|
|
56
|
+
schema: {
|
|
57
|
+
params: Type.Object({
|
|
58
|
+
subject: Type.String(),
|
|
59
|
+
}),
|
|
60
|
+
yieldType: Type.Object({
|
|
61
|
+
seq: Type.Number(),
|
|
62
|
+
message: Type.String(),
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
async function* (ctx, params) {
|
|
67
|
+
// ctx.signal is guaranteed for streams. Pass it to all downstream async work.
|
|
68
|
+
for (let seq = 0; seq < 3; seq++) {
|
|
69
|
+
if (ctx.signal.aborted) return
|
|
70
|
+
yield { seq, message: `event-${params.subject}-${seq}` }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
// HTTP REST — `CreateHttp`. Mounted at GET /api/{{kebab}}/:id
|
|
76
|
+
export const { GetUser } = procs.CreateHttp(
|
|
77
|
+
'GetUser',
|
|
78
|
+
{
|
|
79
|
+
path: '/{{kebab}}/:id',
|
|
80
|
+
method: 'get',
|
|
81
|
+
description: 'Fetch a user (REST).',
|
|
82
|
+
schema: {
|
|
83
|
+
req: {
|
|
84
|
+
pathParams: Type.Object({ id: Type.String() }),
|
|
85
|
+
},
|
|
86
|
+
res: {
|
|
87
|
+
body: Type.Object({
|
|
88
|
+
id: Type.String(),
|
|
89
|
+
name: Type.String(),
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
async (ctx, { pathParams }) => {
|
|
95
|
+
// TODO: implement
|
|
96
|
+
return { id: pathParams.id, name: 'Example' }
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// HTTP stream — `CreateHttpStream`. Mounted at GET /api/{{kebab}}/:id/watch (text/event-stream)
|
|
101
|
+
export const { WatchUser } = procs.CreateHttpStream(
|
|
102
|
+
'WatchUser',
|
|
103
|
+
{
|
|
104
|
+
path: '/{{kebab}}/:id/watch',
|
|
105
|
+
method: 'get',
|
|
106
|
+
description: 'Server-sent stream of user updates (http-stream).',
|
|
107
|
+
schema: {
|
|
108
|
+
req: {
|
|
109
|
+
pathParams: Type.Object({ id: Type.String() }),
|
|
110
|
+
},
|
|
111
|
+
yield: Type.Object({
|
|
112
|
+
id: Type.String(),
|
|
113
|
+
revision: Type.Number(),
|
|
114
|
+
}),
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
async function* (ctx, { pathParams }) {
|
|
118
|
+
for (let revision = 1; revision <= 3; revision++) {
|
|
119
|
+
if (ctx.signal.aborted) return
|
|
120
|
+
yield { id: pathParams.id, revision }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
// ─── Error Taxonomy ───────────────────────────────────────
|
|
126
|
+
// Declare the error classes this service throws. Framework errors
|
|
127
|
+
// (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
|
|
128
|
+
// default taxonomy automatically. Add your own classes here — handlers just
|
|
129
|
+
// `throw` them and the builder serializes via this map. See
|
|
130
|
+
// docs/http-integrations.md#error-handling for the full contract.
|
|
131
|
+
|
|
132
|
+
const {{name}}Errors = defineErrorTaxonomy({
|
|
133
|
+
// Example — replace with your app's error classes:
|
|
134
|
+
// NotFoundError: { class: NotFoundError, statusCode: 404 },
|
|
135
|
+
// AuthError: { class: AuthError, statusCode: 401 },
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// ─── Hono App Builder ─────────────────────────────────────
|
|
139
|
+
// One builder, one register() call, all four kinds. Config is stratified by
|
|
140
|
+
// kind: rpc.{onSuccess}, api.{queryParser, onSuccess}, stream.{...}. Cross-cutting
|
|
141
|
+
// concerns (errors, unknownError, onError, onRequestError, onRequestStart/End,
|
|
142
|
+
// pathPrefix) live at the top level. See src/implementations/http/hono/types.ts.
|
|
143
|
+
|
|
144
|
+
// Optional observers (uncomment as needed):
|
|
145
|
+
// onRequestStart: (c) => {...} // every inbound request (all kinds)
|
|
146
|
+
// rpc: { onSuccess: (procedure, c) => {...} } // successful RPC (Create) responses
|
|
147
|
+
// api: { onSuccess: (procedure, c) => {...} } // successful REST (CreateHttp) responses
|
|
148
|
+
// stream: { onStreamStart: (procedure, c, mode) => {...}, onStreamEnd: (procedure, c, mode) => {...} }
|
|
149
|
+
|
|
150
|
+
const {{name}}Builder = new HonoAppBuilder({
|
|
151
|
+
pathPrefix: '/api',
|
|
152
|
+
errors: {{name}}Errors,
|
|
153
|
+
unknownError: {
|
|
154
|
+
statusCode: 500,
|
|
155
|
+
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
156
|
+
onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
.register(procs, (c) => ({
|
|
160
|
+
userId: c.req.header('x-user-id') || 'anonymous',
|
|
161
|
+
requestId: c.req.header('x-request-id') || crypto.randomUUID(),
|
|
162
|
+
}))
|
|
163
|
+
|
|
164
|
+
export const {{name}}App = {{name}}Builder.build()
|
|
165
|
+
|
|
166
|
+
// Route map:
|
|
167
|
+
// POST /api/{{kebab}}/get-item/1 → RPC (Create)
|
|
168
|
+
// POST /api/{{kebab}}/stream-events/1 → RPC stream (CreateStream, SSE)
|
|
169
|
+
// GET /api/{{kebab}}/:id → REST (CreateHttp)
|
|
170
|
+
// GET /api/{{kebab}}/:id/watch → HTTP stream (CreateHttpStream, SSE)
|
|
171
|
+
|
|
172
|
+
// Documentation: {{name}}Builder.docs or {{name}}Builder.toDocEnvelope()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Test — `{{Name}}.hono.test.ts`
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { describe, test, expect } from 'vitest'
|
|
179
|
+
import { {{name}}App } from './{{Name}}.hono'
|
|
180
|
+
|
|
181
|
+
describe('{{Name}} Hono — all four kinds', () => {
|
|
182
|
+
test('Create (RPC) — POST /api/{{kebab}}/get-item/1', async () => {
|
|
183
|
+
const res = await {{name}}App.request('/api/{{kebab}}/get-item/1', {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: { 'Content-Type': 'application/json' },
|
|
186
|
+
body: JSON.stringify({ id: 'item-1' }),
|
|
187
|
+
})
|
|
188
|
+
expect(res.status).toBe(200)
|
|
189
|
+
expect(await res.json()).toEqual({ id: 'item-1', name: 'Example' })
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('CreateStream (rpc-stream) — POST /api/{{kebab}}/stream-events/1 yields SSE', async () => {
|
|
193
|
+
const res = await {{name}}App.request('/api/{{kebab}}/stream-events/1', {
|
|
194
|
+
method: 'POST',
|
|
195
|
+
headers: { 'Content-Type': 'application/json' },
|
|
196
|
+
body: JSON.stringify({ subject: 'orders' }),
|
|
197
|
+
})
|
|
198
|
+
expect(res.status).toBe(200)
|
|
199
|
+
expect(res.headers.get('content-type') ?? '').toContain('text/event-stream')
|
|
200
|
+
|
|
201
|
+
const text = await res.text()
|
|
202
|
+
// Yielded events are emitted as SSE `data:` frames.
|
|
203
|
+
expect(text).toContain('event-orders-0')
|
|
204
|
+
expect(text).toContain('event-orders-2')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('CreateHttp (REST) — GET /api/{{kebab}}/:id', async () => {
|
|
208
|
+
const res = await {{name}}App.request('/api/{{kebab}}/u-1')
|
|
209
|
+
expect(res.status).toBe(200)
|
|
210
|
+
expect(await res.json()).toEqual({ id: 'u-1', name: 'Example' })
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('CreateHttpStream (http-stream) — GET /api/{{kebab}}/:id/watch yields SSE', async () => {
|
|
214
|
+
const res = await {{name}}App.request('/api/{{kebab}}/u-1/watch')
|
|
215
|
+
expect(res.status).toBe(200)
|
|
216
|
+
expect(res.headers.get('content-type') ?? '').toContain('text/event-stream')
|
|
217
|
+
|
|
218
|
+
const text = await res.text()
|
|
219
|
+
expect(text).toContain('"revision":1')
|
|
220
|
+
expect(text).toContain('"revision":3')
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
```
|
|
@@ -26,18 +26,15 @@ import { ProcedureError, ProcedureValidationError, ProcedureYieldValidationError
|
|
|
26
26
|
// HTTP types
|
|
27
27
|
import type { RPCConfig, RPCHttpRouteDoc, StreamHttpRouteDoc, StreamMode } from 'ts-procedures/http'
|
|
28
28
|
|
|
29
|
-
// Express RPC
|
|
30
|
-
import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
|
|
31
|
-
|
|
32
29
|
// Hono RPC
|
|
33
|
-
import {
|
|
30
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
34
31
|
|
|
35
32
|
// Hono Streaming
|
|
36
|
-
import {
|
|
33
|
+
import { HonoAppBuilder, sse } from 'ts-procedures/hono'
|
|
37
34
|
|
|
38
35
|
// Hono API (REST-style)
|
|
39
|
-
import {
|
|
40
|
-
import type { APIConfig, APIInput } from 'ts-procedures/hono
|
|
36
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
37
|
+
import type { APIConfig, APIInput } from 'ts-procedures/hono'
|
|
41
38
|
|
|
42
39
|
// Doc Registry — compose route docs from multiple builders
|
|
43
40
|
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
@@ -114,31 +111,12 @@ const { StreamEvents } = CreateStream(
|
|
|
114
111
|
)
|
|
115
112
|
```
|
|
116
113
|
|
|
117
|
-
## Express RPC Pattern
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
|
|
121
|
-
import type { RPCConfig } from 'ts-procedures/http'
|
|
122
|
-
|
|
123
|
-
const RPC = Procedures<AppContext, RPCConfig>()
|
|
124
|
-
|
|
125
|
-
RPC.Create('GetUser', {
|
|
126
|
-
scope: 'users', version: 1,
|
|
127
|
-
schema: { params: Type.Object({ id: Type.String() }) },
|
|
128
|
-
}, async (ctx, params) => fetchUser(params.id))
|
|
129
|
-
|
|
130
|
-
const app = new ExpressRPCAppBuilder({ pathPrefix: '/api' })
|
|
131
|
-
.register(RPC, async (req) => ({ userId: await authenticate(req) }))
|
|
132
|
-
.build()
|
|
133
|
-
// POST /api/users/get-user/1
|
|
134
|
-
```
|
|
135
|
-
|
|
136
114
|
## Hono RPC Pattern
|
|
137
115
|
|
|
138
116
|
```typescript
|
|
139
|
-
import {
|
|
117
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
140
118
|
|
|
141
|
-
const app = new
|
|
119
|
+
const app = new HonoAppBuilder({ pathPrefix: '/api' })
|
|
142
120
|
.register(RPC, (c) => ({ userId: c.req.header('x-user-id') }))
|
|
143
121
|
.build()
|
|
144
122
|
```
|
|
@@ -146,7 +124,7 @@ const app = new HonoRPCAppBuilder({ pathPrefix: '/api' })
|
|
|
146
124
|
## Hono Streaming Pattern
|
|
147
125
|
|
|
148
126
|
```typescript
|
|
149
|
-
import {
|
|
127
|
+
import { HonoAppBuilder, sse } from 'ts-procedures/hono'
|
|
150
128
|
|
|
151
129
|
const StreamRPC = Procedures<AppContext, RPCConfig>()
|
|
152
130
|
|
|
@@ -160,43 +138,53 @@ StreamRPC.CreateStream('Feed', {
|
|
|
160
138
|
}
|
|
161
139
|
})
|
|
162
140
|
|
|
163
|
-
const app = new
|
|
141
|
+
const app = new HonoAppBuilder({ defaultStreamMode: 'sse' })
|
|
164
142
|
.register(StreamRPC, (c) => ({ userId: c.req.header('x-user-id') }))
|
|
165
143
|
.build()
|
|
166
144
|
// GET|POST /events/feed/1
|
|
167
145
|
```
|
|
168
146
|
|
|
169
|
-
## Hono API Pattern (REST-style)
|
|
147
|
+
## Hono API Pattern (REST-style) — v8 `CreateHttp`
|
|
148
|
+
|
|
149
|
+
In v8, HTTP routes use `CreateHttp` (not `Create + APIConfig`). `schema.input` is renamed `schema.req`. The factory needs no `APIConfig` type parameter.
|
|
170
150
|
|
|
171
151
|
```typescript
|
|
172
|
-
import {
|
|
173
|
-
import type { APIConfig } from 'ts-procedures/http'
|
|
152
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
174
153
|
|
|
175
|
-
const API = Procedures<AppContext
|
|
154
|
+
const API = Procedures<AppContext>()
|
|
176
155
|
|
|
177
|
-
API.
|
|
156
|
+
API.CreateHttp('GetUser', {
|
|
178
157
|
path: '/users/:id', method: 'get',
|
|
179
158
|
schema: {
|
|
180
|
-
|
|
159
|
+
req: { pathParams: Type.Object({ id: Type.String() }) },
|
|
160
|
+
res: { body: Type.Object({ id: Type.String(), name: Type.String() }) },
|
|
181
161
|
},
|
|
182
162
|
}, async (ctx, { pathParams }) => fetchUser(pathParams.id))
|
|
183
163
|
|
|
184
|
-
API.
|
|
164
|
+
API.CreateHttp('CreateUser', {
|
|
185
165
|
path: '/users', method: 'post',
|
|
186
166
|
schema: {
|
|
187
|
-
|
|
167
|
+
req: { body: Type.Object({ name: Type.String() }) },
|
|
188
168
|
},
|
|
189
169
|
}, async (ctx, { body }) => createUser(body))
|
|
190
170
|
|
|
191
|
-
const app = new
|
|
171
|
+
const app = new HonoAppBuilder({ pathPrefix: '/api' })
|
|
192
172
|
.register(API, (c) => ({ userId: c.req.header('x-user-id') }))
|
|
193
173
|
.build()
|
|
194
174
|
// GET /api/users/:id → 200, POST /api/users → 201
|
|
195
175
|
```
|
|
196
176
|
|
|
177
|
+
**v7 → v8 migration:**
|
|
178
|
+
|
|
179
|
+
1. `Procedures<Ctx, APIConfig>()` → `Procedures<Ctx>()`
|
|
180
|
+
2. `.Create(` → `.CreateHttp(` for HTTP routes
|
|
181
|
+
3. `schema.input` → `schema.req` (same channels: `pathParams`, `query`, `body`, `headers`)
|
|
182
|
+
4. Move response type to `schema.res.body` (optional)
|
|
183
|
+
5. Re-run `npx ts-procedures-codegen`
|
|
184
|
+
|
|
197
185
|
## Astro adapter
|
|
198
186
|
|
|
199
|
-
Catch-all endpoint pattern. Build Hono apps once with
|
|
187
|
+
Catch-all endpoint pattern. Build Hono apps once with `HonoAppBuilder`, mount via `createAstroHandler` in `src/pages/api/[...rest].ts` with `pathPrefix: '/api'`. Read Astro context inside factory closures with `getAstroContext(c)`. Multi-app dispatch is first-non-404-wins.
|
|
200
188
|
|
|
201
189
|
## Error Handling
|
|
202
190
|
|
|
@@ -209,7 +197,7 @@ Catch-all endpoint pattern. Build Hono apps once with HonoAPI/RPC/Stream builder
|
|
|
209
197
|
|
|
210
198
|
### Error Taxonomy (required for custom errors)
|
|
211
199
|
|
|
212
|
-
`defineErrorTaxonomy` maps error classes to HTTP responses declaratively. Works with
|
|
200
|
+
`defineErrorTaxonomy` maps error classes to HTTP responses declaratively. Works with `HonoAppBuilder` for `rpc`, `http`, and pre-stream paths (`rpc-stream` / `http-stream`).
|
|
213
201
|
|
|
214
202
|
Do NOT write `onError` `instanceof` ladders — that's anti-pattern #20. Use the taxonomy instead:
|
|
215
203
|
|
|
@@ -239,13 +227,13 @@ const appErrors = defineErrorTaxonomy({
|
|
|
239
227
|
},
|
|
240
228
|
})
|
|
241
229
|
|
|
242
|
-
new
|
|
230
|
+
new HonoAppBuilder({
|
|
243
231
|
errors: appErrors,
|
|
244
232
|
unknownError: { statusCode: 500, toResponse: () => ({ name: 'InternalServerError' }) },
|
|
245
233
|
})
|
|
246
234
|
```
|
|
247
235
|
|
|
248
|
-
Handlers throw the error classes directly — the builder auto-serializes. `onError` is the first-class imperative peer when you want to handle errors in one callback instead (both modes coexist). In v6 `
|
|
236
|
+
Handlers throw the error classes directly — the builder auto-serializes. `onError` is the first-class imperative peer when you want to handle errors in one callback instead (both modes coexist). In v6 `HonoAppBuilder.onPreStreamError` was renamed to `onError`. Cross-cutting `onRequestError` observer fires for every caught error.
|
|
249
237
|
|
|
250
238
|
### Per-route errors (typed)
|
|
251
239
|
|
|
@@ -313,10 +301,7 @@ onRequestStart → factoryContext() → validation → onStreamStart → handler
|
|
|
313
301
|
- **TypeBox** (`import { Type } from 'typebox'`)
|
|
314
302
|
|
|
315
303
|
**Which HTTP implementation?**
|
|
316
|
-
-
|
|
317
|
-
- Hono (standard) → `HonoRPCAppBuilder`
|
|
318
|
-
- Hono (streaming) → `HonoStreamAppBuilder`
|
|
319
|
-
- Hono (REST-style, per-channel input) → `HonoAPIAppBuilder`
|
|
304
|
+
- Hono (RPC, streams, REST, REST streams) → `HonoAppBuilder`
|
|
320
305
|
|
|
321
306
|
**Stream mode?**
|
|
322
307
|
- Browser EventSource → `'sse'` (default)
|
|
@@ -329,13 +314,14 @@ onRequestStart → factoryContext() → validation → onStreamStart → handler
|
|
|
329
314
|
3. **Never put validation logic in handler** — use `schema.params`
|
|
330
315
|
4. **Never ignore ctx.signal** — pass to all async calls
|
|
331
316
|
5. **Never skip signal.aborted check in stream loops** — causes resource leaks
|
|
332
|
-
6. **Never register Create procedures with
|
|
317
|
+
6. **Never register Create procedures with HonoAppBuilder** — they're silently ignored
|
|
333
318
|
7. **Never use plain JSON Schema objects** — use TypeBox builders
|
|
334
319
|
8. **Never swallow errors without re-throwing** — hides failures
|
|
335
320
|
9. **Never assume extra params fields survive** — `removeAdditional: true` strips them
|
|
336
321
|
10. **Never manually parse types AJV coerces** — `coerceTypes: true` handles it
|
|
337
322
|
11. **Never define both schema.params and schema.input** — mutually exclusive, throws ProcedureRegistrationError
|
|
338
323
|
12. **Never catch raw DOMException/TypeError from generated callables** — catch `ClientTimeoutError`, `ClientAbortError`, `ClientNetworkError` instead; or use `.safe()` for Result-based narrowing
|
|
324
|
+
13. **Never use `Create` for HTTP routes (v8+)** — use `CreateHttp` (unary) or `CreateHttpStream` (streaming); `schema.input` → `schema.req`; `Procedures<Ctx, APIConfig>()` → `Procedures<Ctx>()`
|
|
339
325
|
|
|
340
326
|
## Testing
|
|
341
327
|
|
|
@@ -26,18 +26,15 @@ import { ProcedureError, ProcedureValidationError, ProcedureYieldValidationError
|
|
|
26
26
|
// HTTP types
|
|
27
27
|
import type { RPCConfig, RPCHttpRouteDoc, StreamHttpRouteDoc, StreamMode } from 'ts-procedures/http'
|
|
28
28
|
|
|
29
|
-
// Express RPC
|
|
30
|
-
import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
|
|
31
|
-
|
|
32
29
|
// Hono RPC
|
|
33
|
-
import {
|
|
30
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
34
31
|
|
|
35
32
|
// Hono Streaming
|
|
36
|
-
import {
|
|
33
|
+
import { HonoAppBuilder, sse } from 'ts-procedures/hono'
|
|
37
34
|
|
|
38
35
|
// Hono API (REST-style)
|
|
39
|
-
import {
|
|
40
|
-
import type { APIConfig, APIInput } from 'ts-procedures/hono
|
|
36
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
37
|
+
import type { APIConfig, APIInput } from 'ts-procedures/hono'
|
|
41
38
|
|
|
42
39
|
// Doc Registry — compose route docs from multiple builders
|
|
43
40
|
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
@@ -114,31 +111,12 @@ const { StreamEvents } = CreateStream(
|
|
|
114
111
|
)
|
|
115
112
|
```
|
|
116
113
|
|
|
117
|
-
## Express RPC Pattern
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
|
|
121
|
-
import type { RPCConfig } from 'ts-procedures/http'
|
|
122
|
-
|
|
123
|
-
const RPC = Procedures<AppContext, RPCConfig>()
|
|
124
|
-
|
|
125
|
-
RPC.Create('GetUser', {
|
|
126
|
-
scope: 'users', version: 1,
|
|
127
|
-
schema: { params: Type.Object({ id: Type.String() }) },
|
|
128
|
-
}, async (ctx, params) => fetchUser(params.id))
|
|
129
|
-
|
|
130
|
-
const app = new ExpressRPCAppBuilder({ pathPrefix: '/api' })
|
|
131
|
-
.register(RPC, async (req) => ({ userId: await authenticate(req) }))
|
|
132
|
-
.build()
|
|
133
|
-
// POST /api/users/get-user/1
|
|
134
|
-
```
|
|
135
|
-
|
|
136
114
|
## Hono RPC Pattern
|
|
137
115
|
|
|
138
116
|
```typescript
|
|
139
|
-
import {
|
|
117
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
140
118
|
|
|
141
|
-
const app = new
|
|
119
|
+
const app = new HonoAppBuilder({ pathPrefix: '/api' })
|
|
142
120
|
.register(RPC, (c) => ({ userId: c.req.header('x-user-id') }))
|
|
143
121
|
.build()
|
|
144
122
|
```
|
|
@@ -146,7 +124,7 @@ const app = new HonoRPCAppBuilder({ pathPrefix: '/api' })
|
|
|
146
124
|
## Hono Streaming Pattern
|
|
147
125
|
|
|
148
126
|
```typescript
|
|
149
|
-
import {
|
|
127
|
+
import { HonoAppBuilder, sse } from 'ts-procedures/hono'
|
|
150
128
|
|
|
151
129
|
const StreamRPC = Procedures<AppContext, RPCConfig>()
|
|
152
130
|
|
|
@@ -160,43 +138,53 @@ StreamRPC.CreateStream('Feed', {
|
|
|
160
138
|
}
|
|
161
139
|
})
|
|
162
140
|
|
|
163
|
-
const app = new
|
|
141
|
+
const app = new HonoAppBuilder({ defaultStreamMode: 'sse' })
|
|
164
142
|
.register(StreamRPC, (c) => ({ userId: c.req.header('x-user-id') }))
|
|
165
143
|
.build()
|
|
166
144
|
// GET|POST /events/feed/1
|
|
167
145
|
```
|
|
168
146
|
|
|
169
|
-
## Hono API Pattern (REST-style)
|
|
147
|
+
## Hono API Pattern (REST-style) — v8 `CreateHttp`
|
|
148
|
+
|
|
149
|
+
In v8, HTTP routes use `CreateHttp` (not `Create + APIConfig`). `schema.input` is renamed `schema.req`. The factory needs no `APIConfig` type parameter.
|
|
170
150
|
|
|
171
151
|
```typescript
|
|
172
|
-
import {
|
|
173
|
-
import type { APIConfig } from 'ts-procedures/http'
|
|
152
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
174
153
|
|
|
175
|
-
const API = Procedures<AppContext
|
|
154
|
+
const API = Procedures<AppContext>()
|
|
176
155
|
|
|
177
|
-
API.
|
|
156
|
+
API.CreateHttp('GetUser', {
|
|
178
157
|
path: '/users/:id', method: 'get',
|
|
179
158
|
schema: {
|
|
180
|
-
|
|
159
|
+
req: { pathParams: Type.Object({ id: Type.String() }) },
|
|
160
|
+
res: { body: Type.Object({ id: Type.String(), name: Type.String() }) },
|
|
181
161
|
},
|
|
182
162
|
}, async (ctx, { pathParams }) => fetchUser(pathParams.id))
|
|
183
163
|
|
|
184
|
-
API.
|
|
164
|
+
API.CreateHttp('CreateUser', {
|
|
185
165
|
path: '/users', method: 'post',
|
|
186
166
|
schema: {
|
|
187
|
-
|
|
167
|
+
req: { body: Type.Object({ name: Type.String() }) },
|
|
188
168
|
},
|
|
189
169
|
}, async (ctx, { body }) => createUser(body))
|
|
190
170
|
|
|
191
|
-
const app = new
|
|
171
|
+
const app = new HonoAppBuilder({ pathPrefix: '/api' })
|
|
192
172
|
.register(API, (c) => ({ userId: c.req.header('x-user-id') }))
|
|
193
173
|
.build()
|
|
194
174
|
// GET /api/users/:id → 200, POST /api/users → 201
|
|
195
175
|
```
|
|
196
176
|
|
|
177
|
+
**v7 → v8 migration:**
|
|
178
|
+
|
|
179
|
+
1. `Procedures<Ctx, APIConfig>()` → `Procedures<Ctx>()`
|
|
180
|
+
2. `.Create(` → `.CreateHttp(` for HTTP routes
|
|
181
|
+
3. `schema.input` → `schema.req` (same channels: `pathParams`, `query`, `body`, `headers`)
|
|
182
|
+
4. Move response type to `schema.res.body` (optional)
|
|
183
|
+
5. Re-run `npx ts-procedures-codegen`
|
|
184
|
+
|
|
197
185
|
## Astro adapter
|
|
198
186
|
|
|
199
|
-
Catch-all endpoint pattern. Build Hono apps once with
|
|
187
|
+
Catch-all endpoint pattern. Build Hono apps once with `HonoAppBuilder`, mount via `createAstroHandler` in `src/pages/api/[...rest].ts` with `pathPrefix: '/api'`. Read Astro context inside factory closures with `getAstroContext(c)`. Multi-app dispatch is first-non-404-wins.
|
|
200
188
|
|
|
201
189
|
## Error Handling
|
|
202
190
|
|
|
@@ -209,7 +197,7 @@ Catch-all endpoint pattern. Build Hono apps once with HonoAPI/RPC/Stream builder
|
|
|
209
197
|
|
|
210
198
|
### Error Taxonomy (required for custom errors)
|
|
211
199
|
|
|
212
|
-
`defineErrorTaxonomy` maps error classes to HTTP responses declaratively. Works with
|
|
200
|
+
`defineErrorTaxonomy` maps error classes to HTTP responses declaratively. Works with `HonoAppBuilder` for `rpc`, `http`, and pre-stream paths (`rpc-stream` / `http-stream`).
|
|
213
201
|
|
|
214
202
|
Do NOT write `onError` `instanceof` ladders — that's anti-pattern #20. Use the taxonomy instead:
|
|
215
203
|
|
|
@@ -239,13 +227,13 @@ const appErrors = defineErrorTaxonomy({
|
|
|
239
227
|
},
|
|
240
228
|
})
|
|
241
229
|
|
|
242
|
-
new
|
|
230
|
+
new HonoAppBuilder({
|
|
243
231
|
errors: appErrors,
|
|
244
232
|
unknownError: { statusCode: 500, toResponse: () => ({ name: 'InternalServerError' }) },
|
|
245
233
|
})
|
|
246
234
|
```
|
|
247
235
|
|
|
248
|
-
Handlers throw the error classes directly — the builder auto-serializes. `onError` is the first-class imperative peer when you want to handle errors in one callback instead (both modes coexist). In v6 `
|
|
236
|
+
Handlers throw the error classes directly — the builder auto-serializes. `onError` is the first-class imperative peer when you want to handle errors in one callback instead (both modes coexist). In v6 `HonoAppBuilder.onPreStreamError` was renamed to `onError`. Cross-cutting `onRequestError` observer fires for every caught error.
|
|
249
237
|
|
|
250
238
|
### Per-route errors (typed)
|
|
251
239
|
|
|
@@ -313,10 +301,7 @@ onRequestStart → factoryContext() → validation → onStreamStart → handler
|
|
|
313
301
|
- **TypeBox** (`import { Type } from 'typebox'`)
|
|
314
302
|
|
|
315
303
|
**Which HTTP implementation?**
|
|
316
|
-
-
|
|
317
|
-
- Hono (standard) → `HonoRPCAppBuilder`
|
|
318
|
-
- Hono (streaming) → `HonoStreamAppBuilder`
|
|
319
|
-
- Hono (REST-style, per-channel input) → `HonoAPIAppBuilder`
|
|
304
|
+
- Hono (RPC, streams, REST, REST streams) → `HonoAppBuilder`
|
|
320
305
|
|
|
321
306
|
**Stream mode?**
|
|
322
307
|
- Browser EventSource → `'sse'` (default)
|
|
@@ -329,13 +314,14 @@ onRequestStart → factoryContext() → validation → onStreamStart → handler
|
|
|
329
314
|
3. **Never put validation logic in handler** — use `schema.params`
|
|
330
315
|
4. **Never ignore ctx.signal** — pass to all async calls
|
|
331
316
|
5. **Never skip signal.aborted check in stream loops** — causes resource leaks
|
|
332
|
-
6. **Never register Create procedures with
|
|
317
|
+
6. **Never register Create procedures with HonoAppBuilder** — they're silently ignored
|
|
333
318
|
7. **Never use plain JSON Schema objects** — use TypeBox builders
|
|
334
319
|
8. **Never swallow errors without re-throwing** — hides failures
|
|
335
320
|
9. **Never assume extra params fields survive** — `removeAdditional: true` strips them
|
|
336
321
|
10. **Never manually parse types AJV coerces** — `coerceTypes: true` handles it
|
|
337
322
|
11. **Never define both schema.params and schema.input** — mutually exclusive, throws ProcedureRegistrationError
|
|
338
323
|
12. **Never catch raw DOMException/TypeError from generated callables** — catch `ClientTimeoutError`, `ClientAbortError`, `ClientNetworkError` instead; or use `.safe()` for Result-based narrowing
|
|
324
|
+
13. **Never use `Create` for HTTP routes (v8+)** — use `CreateHttp` (unary) or `CreateHttpStream` (streaming); `schema.input` → `schema.req`; `Procedures<Ctx, APIConfig>()` → `Procedures<Ctx>()`
|
|
339
325
|
|
|
340
326
|
## Testing
|
|
341
327
|
|
package/build/client/call.js
CHANGED
|
@@ -84,7 +84,10 @@ export async function executeCall(config) {
|
|
|
84
84
|
scope: descriptor.scope,
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
// 8. Return the body
|
|
87
|
+
// 8. Return the body (or { body, headers } when the route declares res.headers)
|
|
88
|
+
if (descriptor.responseHeadersDeclared) {
|
|
89
|
+
return { body: response.body, headers: response.headers };
|
|
90
|
+
}
|
|
88
91
|
return response.body;
|
|
89
92
|
}
|
|
90
93
|
/**
|
package/build/client/call.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/client/call.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/client/call.ts"],"names":[],"mappings":"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"}
|
|
@@ -270,6 +270,29 @@ describe('executeCall', () => {
|
|
|
270
270
|
});
|
|
271
271
|
expect(observedMeta).toEqual({ traceId: 'override', attempt: 1 });
|
|
272
272
|
});
|
|
273
|
+
// ── responseHeadersDeclared ──
|
|
274
|
+
it('returns bare body when responseHeadersDeclared is not set', async () => {
|
|
275
|
+
const adapter = makeAdapter({ body: { id: 'abc' } });
|
|
276
|
+
const result = await run({ adapter, descriptor: makeDescriptor() });
|
|
277
|
+
expect(result).toEqual({ id: 'abc' });
|
|
278
|
+
});
|
|
279
|
+
it('returns { body, headers } when responseHeadersDeclared is true', async () => {
|
|
280
|
+
const adapter = {
|
|
281
|
+
request: vi.fn(async () => ({
|
|
282
|
+
status: 200,
|
|
283
|
+
headers: { 'x-rate-limit': '99' },
|
|
284
|
+
body: { id: 'abc' },
|
|
285
|
+
})),
|
|
286
|
+
stream: vi.fn(async () => { throw new Error('not expected'); }),
|
|
287
|
+
};
|
|
288
|
+
const descriptor = makeDescriptor({ responseHeadersDeclared: true });
|
|
289
|
+
const result = await run({
|
|
290
|
+
adapter,
|
|
291
|
+
descriptor,
|
|
292
|
+
});
|
|
293
|
+
expect(result).toEqual({ body: { id: 'abc' }, headers: { 'x-rate-limit': '99' } });
|
|
294
|
+
expect(result.headers['x-rate-limit']).toBe('99');
|
|
295
|
+
});
|
|
273
296
|
});
|
|
274
297
|
describe('executeCall classifier integration', () => {
|
|
275
298
|
it('throws ClientNetworkError when adapter throws TypeError', async () => {
|