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/docs/core.md
CHANGED
|
@@ -93,7 +93,7 @@ async function* (ctx, params) => AsyncGenerator<TYield, TReturn | void>
|
|
|
93
93
|
- `ctx.error(message, meta?)` - Create a ProcedureError
|
|
94
94
|
- `ctx.signal?` - AbortSignal for cancellation support (optional for `Create`, always present for `CreateStream`)
|
|
95
95
|
|
|
96
|
-
When using the built-in HTTP
|
|
96
|
+
When using the built-in HTTP implementation (Hono), `ctx.signal` is automatically injected from the HTTP request, so handlers can detect client disconnection. For direct usage without a server, `signal` is `undefined` unless you pass one in context.
|
|
97
97
|
|
|
98
98
|
**Returns:**
|
|
99
99
|
- `{ [name]: handler }` - Named generator export
|
|
@@ -292,7 +292,7 @@ The taxonomy also drives typed `catch` blocks on generated clients and per-route
|
|
|
292
292
|
|
|
293
293
|
## Abort Signal
|
|
294
294
|
|
|
295
|
-
For regular procedures, `ctx.signal` is available when the server implementation provides it. The built-in HTTP
|
|
295
|
+
For regular procedures, `ctx.signal` is available when the server implementation provides it. The built-in HTTP integration (`HonoAppBuilder`) injects the request's abort signal automatically:
|
|
296
296
|
|
|
297
297
|
```typescript
|
|
298
298
|
const { Create } = Procedures<{ signal: AbortSignal }>()
|
|
@@ -310,7 +310,7 @@ const { LongQuery } = Create(
|
|
|
310
310
|
)
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
-
When using the Hono
|
|
313
|
+
When using the Hono implementation, `ctx.signal` aborts when the client disconnects, automatically cancelling in-flight `fetch()` calls, database queries, or any other signal-aware operation.
|
|
314
314
|
|
|
315
315
|
For streaming abort signal behavior (including `signal.reason` and consumer break detection), see [Streaming Procedures](./streaming.md#abort-signal-integration).
|
|
316
316
|
|
|
@@ -318,27 +318,25 @@ For streaming abort signal behavior (including `signal.reason` and consumer brea
|
|
|
318
318
|
|
|
319
319
|
### onCreate Callback
|
|
320
320
|
|
|
321
|
-
Register procedures with
|
|
321
|
+
Register procedures with a custom framework via the `onCreate` callback. Each registration receives `{ name, handler, config }` so you can wire it into whatever router or framework you want:
|
|
322
322
|
|
|
323
323
|
```typescript
|
|
324
|
-
import
|
|
324
|
+
import { Hono } from 'hono'
|
|
325
|
+
import { ProcedureError } from 'ts-procedures'
|
|
325
326
|
|
|
326
|
-
const app =
|
|
327
|
-
const routes: Map<string, Function> = new Map()
|
|
327
|
+
const app = new Hono()
|
|
328
328
|
|
|
329
|
-
const { Create } = Procedures<{
|
|
330
|
-
onCreate: ({ name, handler
|
|
331
|
-
|
|
332
|
-
app.post(`/rpc/${name}`, async (req, res) => {
|
|
329
|
+
const { Create } = Procedures<{ raw: unknown }>({
|
|
330
|
+
onCreate: ({ name, handler }) => {
|
|
331
|
+
app.post(`/rpc/${name}`, async (c) => {
|
|
333
332
|
try {
|
|
334
|
-
const result = await handler({
|
|
335
|
-
|
|
333
|
+
const result = await handler({ raw: c }, await c.req.json())
|
|
334
|
+
return c.json(result)
|
|
336
335
|
} catch (e) {
|
|
337
336
|
if (e instanceof ProcedureError) {
|
|
338
|
-
|
|
339
|
-
} else {
|
|
340
|
-
res.status(500).json({ error: 'Internal error' })
|
|
337
|
+
return c.json({ error: e.message }, 500)
|
|
341
338
|
}
|
|
339
|
+
return c.json({ error: 'Internal error' }, 500)
|
|
342
340
|
}
|
|
343
341
|
})
|
|
344
342
|
},
|
|
@@ -347,7 +345,7 @@ const { Create } = Procedures<{ req: Request; res: Response }>({
|
|
|
347
345
|
// Procedures are automatically registered as /rpc/GetUser, /rpc/CreateUser, etc.
|
|
348
346
|
```
|
|
349
347
|
|
|
350
|
-
For built-in HTTP
|
|
348
|
+
For the built-in HTTP integration (RPC, RPC streams, REST, REST streams via `HonoAppBuilder`), see [HTTP Integrations](./http-integrations.md).
|
|
351
349
|
|
|
352
350
|
### Introspection with getProcedures()
|
|
353
351
|
|
|
@@ -2,89 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
# HTTP Framework Integrations
|
|
4
4
|
|
|
5
|
-
ts-procedures includes built-in HTTP
|
|
5
|
+
ts-procedures includes a built-in HTTP builder for Hono that handles routing, context resolution, lifecycle hooks, abort signals, and automatic route documentation. The builder reads procedures off the factory at registration time and mounts the matching routes when you call `build()`.
|
|
6
6
|
|
|
7
|
-
For a full
|
|
7
|
+
For a full reference (config interfaces, path generation, context resolution, abort signal, lifecycle hooks, route documentation), see the [HTTP implementations overview](../src/implementations/http/README.md).
|
|
8
8
|
|
|
9
9
|
## Integrations at a Glance
|
|
10
10
|
|
|
11
|
-
| Integration | Export |
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
| Hono RPC | `ts-procedures/hono-rpc` | RPC (POST routes) | [README](../src/implementations/http/hono-rpc/README.md) |
|
|
15
|
-
| Hono Stream | `ts-procedures/hono-stream` | SSE/text streaming | [README](../src/implementations/http/hono-stream/README.md) |
|
|
16
|
-
| Hono API | `ts-procedures/hono-api` | REST (method-based) | [README](../src/implementations/http/hono-api/README.md) |
|
|
11
|
+
| Integration | Export | Procedure kinds | Guide |
|
|
12
|
+
|-------------|--------|-----------------|-------|
|
|
13
|
+
| HonoAppBuilder | `ts-procedures/hono` | `rpc`, `rpc-stream`, `http`, `http-stream` | [HTTP implementations overview](../src/implementations/http/README.md) |
|
|
17
14
|
|
|
18
|
-
##
|
|
15
|
+
## Hono
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
`HonoAppBuilder` is the single Hono integration. One builder dispatches all four procedure kinds — `rpc`, `rpc-stream`, `http`, `http-stream` — from one `register()` call. Works with Bun, Deno, Cloudflare Workers, and Node.js.
|
|
21
18
|
|
|
22
19
|
```typescript
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
RPC.Create(
|
|
28
|
-
'GetUser',
|
|
29
|
-
{
|
|
30
|
-
scope: ['users', 'get'],
|
|
31
|
-
version: 1,
|
|
32
|
-
schema: {
|
|
33
|
-
params: Type.Object({ id: Type.String() }),
|
|
34
|
-
returnType: Type.Object({ id: Type.String(), name: Type.String() }),
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
async (ctx, params) => {
|
|
38
|
-
return { id: params.id, name: 'John Doe' }
|
|
39
|
-
}
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
const app = new ExpressRPCAppBuilder()
|
|
43
|
-
.register(RPC, (req) => ({ userId: req.headers['x-user-id'] as string }))
|
|
44
|
-
.build()
|
|
45
|
-
|
|
46
|
-
app.listen(3000)
|
|
47
|
-
// Route created: POST /users/get/get-user/1
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
See the [Express RPC Integration Guide](../src/implementations/http/express-rpc/README.md) for complete setup including lifecycle hooks, error handling, and route documentation.
|
|
51
|
-
|
|
52
|
-
## Hono RPC
|
|
53
|
-
|
|
54
|
-
RPC-style HTTP integration for Hono with the same routing pattern as Express RPC. Works with Bun, Deno, Cloudflare Workers, and Node.js.
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import { HonoRPCAppBuilder, RPCConfig } from 'ts-procedures/hono-rpc'
|
|
20
|
+
import { Procedures } from 'ts-procedures'
|
|
21
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
22
|
+
import type { RPCConfig } from 'ts-procedures/http'
|
|
23
|
+
import { Type } from 'typebox'
|
|
58
24
|
|
|
59
|
-
|
|
25
|
+
type AppContext = { userId: string }
|
|
26
|
+
const procs = Procedures<AppContext, RPCConfig>()
|
|
60
27
|
|
|
61
|
-
RPC
|
|
28
|
+
// 1. RPC procedure (Create) — POST /rpc/users/profile/get-user/1
|
|
29
|
+
procs.Create('GetUser', {
|
|
62
30
|
scope: ['users', 'profile'],
|
|
63
31
|
version: 1,
|
|
64
32
|
schema: { params: Type.Object({ id: Type.String() }) },
|
|
65
|
-
}, async (ctx, params) => {
|
|
66
|
-
return { id: params.id, name: 'John Doe' }
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
const app = new HonoRPCAppBuilder({ pathPrefix: '/rpc' })
|
|
70
|
-
.register(RPC, (c) => ({ userId: c.req.header('x-user-id') || 'anonymous' }))
|
|
71
|
-
.build()
|
|
72
|
-
|
|
73
|
-
// POST /rpc/users/profile/get-user/1
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
See the [Hono RPC Integration Guide](../src/implementations/http/hono-rpc/README.md) for complete setup including context resolution, doc extension, abort signal, error handling, and runtime compatibility.
|
|
77
|
-
|
|
78
|
-
## Hono Stream
|
|
33
|
+
}, async (ctx, params) => ({ id: params.id, name: 'John Doe' }))
|
|
79
34
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { HonoStreamAppBuilder } from 'ts-procedures/hono-stream'
|
|
84
|
-
|
|
85
|
-
const StreamRPC = Procedures<AppContext, RPCConfig>()
|
|
86
|
-
|
|
87
|
-
StreamRPC.CreateStream('WatchNotifications', {
|
|
35
|
+
// 2. RPC stream (CreateStream) — GET/POST /rpc/user/notifications/watch-notifications/1
|
|
36
|
+
procs.CreateStream('WatchNotifications', {
|
|
88
37
|
scope: ['user', 'notifications'],
|
|
89
38
|
version: 1,
|
|
90
39
|
schema: { yieldType: Type.Object({ id: Type.Number(), message: Type.String() }) },
|
|
@@ -95,60 +44,43 @@ StreamRPC.CreateStream('WatchNotifications', {
|
|
|
95
44
|
}
|
|
96
45
|
})
|
|
97
46
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.build()
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
See the [Hono Stream Integration Guide](../src/implementations/http/hono-stream/README.md) for complete setup including SSE/text modes, the `sse()` helper, validation, error handling, and migration notes.
|
|
104
|
-
|
|
105
|
-
## Hono API Integration
|
|
106
|
-
|
|
107
|
-
REST-style HTTP integration for Hono that routes by HTTP method with per-channel input validation via `schema.input`.
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import { Procedures } from 'ts-procedures'
|
|
111
|
-
import { HonoAPIAppBuilder } from 'ts-procedures/hono-api'
|
|
112
|
-
import type { APIConfig } from 'ts-procedures/http'
|
|
113
|
-
import { Type } from 'typebox'
|
|
114
|
-
|
|
115
|
-
const API = Procedures<{ userId: string }, APIConfig>()
|
|
116
|
-
|
|
117
|
-
API.Create('GetUser', {
|
|
47
|
+
// 3. REST procedure (CreateHttp) — GET /rpc/users/:id
|
|
48
|
+
procs.CreateHttp('GetUserRest', {
|
|
118
49
|
path: '/users/:id',
|
|
119
50
|
method: 'get',
|
|
51
|
+
scope: 'users',
|
|
120
52
|
schema: {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
},
|
|
124
|
-
returnType: Type.Object({ id: Type.String(), name: Type.String() }),
|
|
53
|
+
req: { pathParams: Type.Object({ id: Type.String() }) },
|
|
54
|
+
res: { body: Type.Object({ id: Type.String(), name: Type.String() }) },
|
|
125
55
|
},
|
|
126
|
-
}, async (ctx, { pathParams }) =>
|
|
127
|
-
return await fetchUser(pathParams.id)
|
|
128
|
-
})
|
|
56
|
+
}, async (ctx, { pathParams }) => fetchUser(pathParams.id))
|
|
129
57
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
58
|
+
// 4. REST stream (CreateHttpStream) — GET /rpc/streams/logs
|
|
59
|
+
procs.CreateHttpStream('TailLogs', {
|
|
60
|
+
path: '/streams/logs',
|
|
61
|
+
method: 'get',
|
|
62
|
+
scope: 'streams',
|
|
133
63
|
schema: {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
},
|
|
64
|
+
req: { query: Type.Object({ source: Type.String() }) },
|
|
65
|
+
yield: Type.Object({ line: Type.String() }),
|
|
137
66
|
},
|
|
138
|
-
}, async (ctx, {
|
|
139
|
-
|
|
67
|
+
}, async function* (ctx, { query }) {
|
|
68
|
+
for await (const line of tail(query.source)) yield { line }
|
|
140
69
|
})
|
|
141
70
|
|
|
142
|
-
const app = new
|
|
143
|
-
|
|
71
|
+
const app = new HonoAppBuilder({
|
|
72
|
+
pathPrefix: '/rpc',
|
|
73
|
+
rpc: { onSuccess: (proc, c) => log.info(`[rpc] ${proc.name}`) },
|
|
74
|
+
api: { onSuccess: (proc, c) => log.info(`[api] ${proc.name}`) },
|
|
75
|
+
stream: { defaultStreamMode: 'sse' },
|
|
76
|
+
})
|
|
77
|
+
.register(procs, (c) => ({ userId: c.req.header('x-user-id') || 'anonymous' }))
|
|
144
78
|
.build()
|
|
145
|
-
|
|
146
|
-
// Routes:
|
|
147
|
-
// GET /api/users/:id -> 200
|
|
148
|
-
// POST /api/users -> 201
|
|
149
79
|
```
|
|
150
80
|
|
|
151
|
-
|
|
81
|
+
`HonoAppBuilder`'s config is **stratified** — kind-specific knobs (`onSuccess`, `queryParser`, `defaultStreamMode`, `onStreamStart`, `onStreamEnd`, `onMidStreamError`) live inside the matching `rpc:` / `api:` / `stream:` block. Cross-cutting hooks (`onRequestStart`, `onRequestEnd`, `onRequestError`, `errors`, `unknownError`, `onError`) stay at the top level.
|
|
82
|
+
|
|
83
|
+
For the full surface — config interfaces, path generation, context resolution, abort signal, lifecycle hooks, route doc shapes — see the [HTTP implementations overview](../src/implementations/http/README.md).
|
|
152
84
|
|
|
153
85
|
## Error Handling
|
|
154
86
|
|
|
@@ -165,7 +97,7 @@ Both modes also expose `onRequestError` — a cross-cutting observer for logging
|
|
|
165
97
|
|
|
166
98
|
```typescript
|
|
167
99
|
import { defineErrorTaxonomy } from 'ts-procedures/http-errors'
|
|
168
|
-
import {
|
|
100
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
169
101
|
|
|
170
102
|
class UseCaseError extends Error {
|
|
171
103
|
constructor(readonly externalMsg: string, readonly internalMsg: string) {
|
|
@@ -190,40 +122,34 @@ const appErrors = defineErrorTaxonomy({
|
|
|
190
122
|
},
|
|
191
123
|
})
|
|
192
124
|
|
|
193
|
-
new
|
|
125
|
+
new HonoAppBuilder({
|
|
194
126
|
errors: appErrors,
|
|
195
127
|
unknownError: {
|
|
196
128
|
statusCode: 500,
|
|
197
129
|
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
198
130
|
onCatch: (err, { procedure }) => logger.error({ procedure: procedure.name, err }),
|
|
199
131
|
},
|
|
200
|
-
}).register(
|
|
132
|
+
}).register(procs, /* ... */).build()
|
|
201
133
|
```
|
|
202
134
|
|
|
203
|
-
|
|
135
|
+
For streaming procedures the taxonomy covers the pre-stream path only; mid-stream errors are handled via `stream.onMidStreamError`.
|
|
204
136
|
|
|
205
137
|
### Imperative — the `onError` callback
|
|
206
138
|
|
|
207
139
|
For apps that don't need typed client dispatch or declarative docs, configure `onError` directly and handle every error in one place:
|
|
208
140
|
|
|
209
141
|
```typescript
|
|
210
|
-
new
|
|
142
|
+
new HonoAppBuilder({
|
|
211
143
|
onError: (procedure, c, error) => {
|
|
212
144
|
logger.error(`[${procedure.name}]`, error)
|
|
213
145
|
return c.json({ error: error.message ?? 'unknown error' }, 500)
|
|
214
146
|
},
|
|
215
|
-
}).register(
|
|
147
|
+
}).register(procs, /* ... */).build()
|
|
216
148
|
```
|
|
217
149
|
|
|
218
150
|
(Need to route different error classes to different status codes? Use the taxonomy — that's exactly what it's designed for. Hand-writing `instanceof` ladders inside `onError` is [anti-pattern #20](../agent_config/claude-code/skills/ts-procedures/anti-patterns.md).)
|
|
219
151
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
| Builder | `onError` signature |
|
|
223
|
-
|---|---|
|
|
224
|
-
| `HonoAPIAppBuilder`, `HonoRPCAppBuilder` | `(procedure, c: Context, error) => Response \| Promise<Response>` |
|
|
225
|
-
| `ExpressRPCAppBuilder` | `(procedure, req, res, error) => void` (write to `res`) |
|
|
226
|
-
| `HonoStreamAppBuilder` | `(procedure, c: Context, error) => Response \| Promise<Response>` — pre-stream only |
|
|
152
|
+
`HonoAppBuilder`'s `onError` signature: `(procedure, c: Context, error) => Response | Promise<Response>` — for streaming procedures, pre-stream only.
|
|
227
153
|
|
|
228
154
|
Picking between modes isn't irreversible: you can start with `onError`, migrate chunks to the taxonomy as your error model stabilizes, and leave the rest in `onError`. Both modes coexist in the dispatch order below.
|
|
229
155
|
|
|
@@ -232,27 +158,27 @@ Picking between modes isn't irreversible: you can start with `onError`, migrate
|
|
|
232
158
|
`APIConfig` and `RPCConfig` are generic over a `TErrorKey extends string` parameter so you can declare which errors a specific route may emit:
|
|
233
159
|
|
|
234
160
|
```typescript
|
|
235
|
-
|
|
161
|
+
const procs = Procedures<Ctx>()
|
|
162
|
+
type ErrorKey = keyof typeof appErrors & string
|
|
236
163
|
|
|
237
|
-
|
|
238
|
-
const API = Procedures<Ctx, MyAPIConfig>()
|
|
239
|
-
|
|
240
|
-
API.Create('GetUser', {
|
|
164
|
+
procs.CreateHttp('GetUser', {
|
|
241
165
|
path: '/users/:id',
|
|
242
166
|
method: 'get',
|
|
243
|
-
errors: ['UseCaseError'], // typo-checked against the taxonomy keys
|
|
167
|
+
errors: ['UseCaseError'] satisfies ErrorKey[], // typo-checked against the taxonomy keys
|
|
244
168
|
schema: { /* ... */ },
|
|
245
169
|
}, /* handler */)
|
|
246
170
|
```
|
|
247
171
|
|
|
248
172
|
These per-route declarations flow into the DocEnvelope and drive typed `catch` blocks on generated clients — see [Client & Codegen → Typed Error Handling](./client-and-codegen.md#typed-error-handling).
|
|
249
173
|
|
|
174
|
+
**Tip:** if a factory exclusively contains HTTP procedures, prefer the factory-level generic form `Procedures<Ctx, APIConfig<keyof typeof appErrors & string>>()` — it bakes the typo-checked error keys into every route on that factory so individual `errors: [...] satisfies ErrorKey[]` annotations are no longer needed. The per-route `satisfies` form above is the right pick when a single factory mixes HTTP procedures with other config shapes.
|
|
175
|
+
|
|
250
176
|
### Cross-cutting observability — `onRequestError`
|
|
251
177
|
|
|
252
178
|
`onRequestError` fires for every caught error, **before** dispatch. It's an observer — it can't mutate the response. Use it for APM, distributed tracing, custom logging, or metrics where you want one hook that sees every error regardless of which mode dispatched it.
|
|
253
179
|
|
|
254
180
|
```typescript
|
|
255
|
-
new
|
|
181
|
+
new HonoAppBuilder({
|
|
256
182
|
errors: appErrors,
|
|
257
183
|
onRequestError: async ({ err, procedure, raw }) => {
|
|
258
184
|
sentry.captureException(err, { procedure: procedure.name })
|
|
@@ -271,41 +197,19 @@ The observer is awaited before the response is sent, and any error it throws is
|
|
|
271
197
|
|
|
272
198
|
Configuring only the taxonomy, only `onError`, both, or neither are all valid. When neither is configured the builder goes straight from step 1 to step 4.
|
|
273
199
|
|
|
274
|
-
##
|
|
200
|
+
## DocRegistry — Composing Docs
|
|
275
201
|
|
|
276
|
-
|
|
202
|
+
For a single-builder app, the simplest path is `builder.toDocEnvelope({ basePath, errors })` — it wraps `DocRegistry` for you and produces the same envelope shape:
|
|
277
203
|
|
|
278
204
|
```typescript
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
import { HonoStreamAppBuilder } from 'ts-procedures/hono-stream'
|
|
283
|
-
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
284
|
-
|
|
285
|
-
const app = new Hono()
|
|
286
|
-
|
|
287
|
-
const rpcBuilder = new HonoRPCAppBuilder({ app, pathPrefix: '/rpc' })
|
|
288
|
-
.register(RPC, ctxResolver)
|
|
289
|
-
const apiBuilder = new HonoAPIAppBuilder({ app, pathPrefix: '/api' })
|
|
290
|
-
.register(API, ctxResolver)
|
|
291
|
-
const streamBuilder = new HonoStreamAppBuilder({ app })
|
|
292
|
-
.register(Stream, ctxResolver)
|
|
205
|
+
const builder = new HonoAppBuilder({ pathPrefix: '/api', errors: appErrors })
|
|
206
|
+
.register(procs, ctxResolver)
|
|
207
|
+
const app = builder.build()
|
|
293
208
|
|
|
294
|
-
|
|
295
|
-
apiBuilder.build()
|
|
296
|
-
streamBuilder.build()
|
|
297
|
-
|
|
298
|
-
const docs = new DocRegistry().from(rpcBuilder).from(apiBuilder).from(streamBuilder)
|
|
299
|
-
app.get('/docs', (c) => c.json(docs.toJSON()))
|
|
209
|
+
app.get('/docs', (c) => c.json(builder.toDocEnvelope()))
|
|
300
210
|
```
|
|
301
211
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
If you omit `app`, each builder constructs its own internal `Hono` instance — useful when you really do want isolated apps (multi-tenant routing, separate ports, etc.).
|
|
305
|
-
|
|
306
|
-
## DocRegistry — Composing Docs from Multiple Builders
|
|
307
|
-
|
|
308
|
-
Use `DocRegistry` to compose route documentation from any combination of HTTP builders into a typed envelope:
|
|
212
|
+
For multi-app aggregation — e.g., two `HonoAppBuilder` instances mounted under different prefixes — use `DocRegistry` directly:
|
|
309
213
|
|
|
310
214
|
```typescript
|
|
311
215
|
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
@@ -315,9 +219,8 @@ const docs = new DocRegistry({
|
|
|
315
219
|
headers: [{ name: 'Authorization', description: 'Bearer token', required: false }],
|
|
316
220
|
errors: appErrors, // your ErrorTaxonomy — auto-converted to ErrorDocs, framework defaults merged
|
|
317
221
|
})
|
|
318
|
-
.from(
|
|
319
|
-
.from(
|
|
320
|
-
.from(streamBuilder)
|
|
222
|
+
.from(publicBuilder)
|
|
223
|
+
.from(adminBuilder)
|
|
321
224
|
|
|
322
225
|
app.get('/docs', (c) => c.json(docs.toJSON()))
|
|
323
226
|
```
|
|
@@ -328,7 +231,7 @@ For errors that aren't in your taxonomy (middleware-level, infrastructure, doc-o
|
|
|
328
231
|
|
|
329
232
|
```typescript
|
|
330
233
|
const docs = new DocRegistry({ errors: appErrors, basePath: '/api' })
|
|
331
|
-
.from(
|
|
234
|
+
.from(honoBuilder)
|
|
332
235
|
.documentError({ name: 'RateLimitExceeded', statusCode: 429, description: 'too many requests' })
|
|
333
236
|
```
|
|
334
237
|
|
|
@@ -339,10 +242,20 @@ The `DocRegistry` output is the input for [Client Code Generation](./client-and-
|
|
|
339
242
|
## Type Exports
|
|
340
243
|
|
|
341
244
|
```typescript
|
|
342
|
-
// HTTP types
|
|
343
|
-
import type {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
245
|
+
// Shared HTTP types
|
|
246
|
+
import type {
|
|
247
|
+
RPCConfig,
|
|
248
|
+
RPCHttpRouteDoc,
|
|
249
|
+
StreamHttpRouteDoc,
|
|
250
|
+
HttpStreamRouteDoc,
|
|
251
|
+
StreamMode,
|
|
252
|
+
APIConfig,
|
|
253
|
+
APIHttpRouteDoc,
|
|
254
|
+
APIInput,
|
|
255
|
+
HttpMethod,
|
|
256
|
+
} from 'ts-procedures/http'
|
|
257
|
+
|
|
258
|
+
// Hono builder + Hono-specific types
|
|
259
|
+
import { HonoAppBuilder } from 'ts-procedures/hono'
|
|
260
|
+
import type { HonoAppBuilderConfig, QueryParser, OnRequestErrorContext, ExtendProcedureDoc } from 'ts-procedures/hono'
|
|
348
261
|
```
|
package/docs/streaming.md
CHANGED
|
@@ -105,72 +105,15 @@ for await (const item of CancellableStream({}, {})) {
|
|
|
105
105
|
}
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
## SSE Integration
|
|
108
|
+
## SSE Integration
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
import express from 'express'
|
|
112
|
-
import { Procedures } from 'ts-procedures'
|
|
113
|
-
|
|
114
|
-
const app = express()
|
|
115
|
-
|
|
116
|
-
const { CreateStream, getProcedures } = Procedures<{ req: express.Request }>({
|
|
117
|
-
onCreate: (proc) => {
|
|
118
|
-
if (proc.isStream) {
|
|
119
|
-
// Register streaming procedures as SSE endpoints
|
|
120
|
-
app.get(`/stream/${proc.name}`, async (req, res) => {
|
|
121
|
-
res.writeHead(200, {
|
|
122
|
-
'Content-Type': 'text/event-stream',
|
|
123
|
-
'Cache-Control': 'no-cache',
|
|
124
|
-
'Connection': 'keep-alive',
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const generator = proc.handler({ req }, req.query)
|
|
128
|
-
|
|
129
|
-
req.on('close', async () => {
|
|
130
|
-
// Client disconnected - stop the generator
|
|
131
|
-
await generator.return(undefined)
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
for await (const data of generator) {
|
|
136
|
-
res.write(`data: ${JSON.stringify(data)}\n\n`)
|
|
137
|
-
}
|
|
138
|
-
} finally {
|
|
139
|
-
res.end()
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// Define a streaming procedure
|
|
147
|
-
CreateStream(
|
|
148
|
-
'LiveFeed',
|
|
149
|
-
{
|
|
150
|
-
schema: {
|
|
151
|
-
params: Type.Object({ channel: Type.String() }),
|
|
152
|
-
yieldType: Type.Object({ event: Type.String(), data: Type.Any() }),
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
async function* (ctx, params) {
|
|
156
|
-
while (!ctx.signal.aborted) {
|
|
157
|
-
const event = await pollForEvent(params.channel)
|
|
158
|
-
yield event
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
app.listen(3000)
|
|
164
|
-
// SSE endpoint: GET /stream/LiveFeed?channel=updates
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
For the built-in Hono streaming integration, see the [Hono Stream README](../src/implementations/http/hono-stream/README.md).
|
|
110
|
+
For the built-in Hono streaming integration, see [HTTP Integrations](./http-integrations.md). `HonoAppBuilder` dispatches `CreateStream` and `CreateHttpStream` procedures as SSE or text streams from the same `register()` call as your RPC and REST routes.
|
|
168
111
|
|
|
169
112
|
## Stream Errors
|
|
170
113
|
|
|
171
114
|
Streaming procedures support the same error handling as regular procedures (see [Error Handling](./core.md#error-handling)).
|
|
172
115
|
|
|
173
|
-
For HTTP stream endpoints, pre-stream errors (validation, context resolution, anything thrown before the first yield) go through the same two peer error-handling modes as every other HTTP builder — either the declarative `errors` / `unknownError` taxonomy (from `defineErrorTaxonomy`) or the imperative `onError` callback on `
|
|
116
|
+
For HTTP stream endpoints, pre-stream errors (validation, context resolution, anything thrown before the first yield) go through the same two peer error-handling modes as every other HTTP builder — either the declarative `errors` / `unknownError` taxonomy (from `defineErrorTaxonomy`) or the imperative `onError` callback on `HonoAppBuilder`. Cross-cutting `onRequestError` observer fires for every pre-stream error. See [HTTP Integrations → Error Handling](./http-integrations.md#error-handling) for the full contract. Mid-stream errors (thrown after the first yield) still flow through `onMidStreamError` and are surfaced to the client as SSE error events — the HTTP status is already committed once streaming begins, so mid-stream is outside the peer error modes.
|
|
174
117
|
|
|
175
118
|
```typescript
|
|
176
119
|
const { StreamWithErrors } = CreateStream(
|
|
@@ -60,7 +60,6 @@ Open `package.json` and confirm both:
|
|
|
60
60
|
"optionalDependencies": {
|
|
61
61
|
"ajsc": "7.2.0",
|
|
62
62
|
"astro": "^5.0.0",
|
|
63
|
-
"express": "^5.2.1",
|
|
64
63
|
"hono": "^4.7.4"
|
|
65
64
|
},
|
|
66
65
|
"devDependencies": {
|
|
@@ -990,7 +989,6 @@ Dispatch rules:
|
|
|
990
989
|
|
|
991
990
|
## What's NOT included
|
|
992
991
|
|
|
993
|
-
- Express RPC support (Express uses Node `req`/`res`, not Web Fetch).
|
|
994
992
|
- DocRegistry coupling — wire `DocRegistry` against the same builders separately for client codegen.
|
|
995
993
|
````
|
|
996
994
|
|
|
@@ -1253,7 +1251,7 @@ Multi-app: pass an array; first non-404 response wins. All-404 falls back to the
|
|
|
1253
1251
|
|
|
1254
1252
|
Constraints:
|
|
1255
1253
|
- Astro 5+ in SSR (or `prerender = false`).
|
|
1256
|
-
-
|
|
1254
|
+
- Hono builders only — the adapter accepts `Request`/`Response` apps.
|
|
1257
1255
|
````
|
|
1258
1256
|
|
|
1259
1257
|
- [ ] **Step 3: Update patterns.md**
|
|
@@ -1270,8 +1268,6 @@ When a downstream developer is building inside an Astro app, recommend the catch
|
|
|
1270
1268
|
3. Mount the built app(s) in a single catch-all file: `src/pages/api/[...rest].ts`. Use `pathPrefix: '/api'` so your Hono routes don't need to repeat the prefix.
|
|
1271
1269
|
|
|
1272
1270
|
Anti-pattern: don't create one Astro file per procedure — that loses the centralized factory wiring and forces the developer to repeat the context closure.
|
|
1273
|
-
|
|
1274
|
-
Anti-pattern: don't try to use `express-rpc` with the Astro adapter. The adapter only accepts Web-Fetch apps (Hono).
|
|
1275
1271
|
````
|
|
1276
1272
|
|
|
1277
1273
|
- [ ] **Step 4: Mirror the same content into Copilot and Cursor rules**
|
|
@@ -1283,7 +1279,7 @@ In each file, locate the existing builders section and append (verbatim — same
|
|
|
1283
1279
|
```markdown
|
|
1284
1280
|
## Astro adapter
|
|
1285
1281
|
|
|
1286
|
-
Catch-all endpoint pattern. Build Hono apps once with HonoAPI/RPC/Stream builders, 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.
|
|
1282
|
+
Catch-all endpoint pattern. Build Hono apps once with HonoAPI/RPC/Stream builders, 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.
|
|
1287
1283
|
```
|
|
1288
1284
|
|
|
1289
1285
|
- [ ] **Step 5: Run the docs-consistency check**
|
|
@@ -1388,7 +1384,6 @@ If nothing changed, skip this step.
|
|
|
1388
1384
|
- `stripPrefix(request, prefix)` — defined Task 2, called Task 4. Same signature.
|
|
1389
1385
|
|
|
1390
1386
|
**Out-of-scope items confirmed not in plan:**
|
|
1391
|
-
- Express adapter — explicitly excluded
|
|
1392
1387
|
- Native Astro builders — explicitly excluded
|
|
1393
1388
|
- DocRegistry coupling — wired separately in walkthrough (Task 10), not in adapter
|
|
1394
1389
|
- `dispatch: 'merge'` knob — not introduced
|