ts-procedures 8.5.0 → 9.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 +166 -101
- package/agent_config/claude-code/.claude-plugin/plugin.json +1 -1
- package/agent_config/claude-code/agents/ts-procedures-architect.md +11 -10
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +25 -12
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +10 -12
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +141 -45
- package/agent_config/claude-code/skills/ts-procedures/checklist.md +7 -6
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +45 -6
- package/agent_config/claude-code/skills/ts-procedures/templates/client.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures/templates/hono.md +1 -1
- package/agent_config/copilot/copilot-instructions.md +50 -33
- package/agent_config/cursor/cursorrules +50 -33
- package/build/adapters/astro/astro-context.js.map +1 -0
- package/build/adapters/astro/create-handler.js.map +1 -0
- package/build/adapters/astro/index.js.map +1 -0
- package/build/{implementations/http → adapters}/astro/index.test.js +1 -1
- package/build/adapters/astro/index.test.js.map +1 -0
- package/build/adapters/astro/rewrite-request.js.map +1 -0
- package/build/adapters/hono/envelope-parity.test.js +98 -0
- package/build/adapters/hono/envelope-parity.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http-stream.d.ts +1 -1
- package/build/adapters/hono/handlers/http-stream.js +55 -0
- package/build/adapters/hono/handlers/http-stream.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.js +1 -1
- package/build/adapters/hono/handlers/http-stream.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http.d.ts +1 -1
- package/build/adapters/hono/handlers/http.js +50 -0
- package/build/adapters/hono/handlers/http.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http.test.js +1 -1
- package/build/adapters/hono/handlers/http.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/rpc.d.ts +2 -2
- package/build/adapters/hono/handlers/rpc.js +23 -0
- package/build/adapters/hono/handlers/rpc.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/rpc.test.js +1 -1
- package/build/adapters/hono/handlers/rpc.test.js.map +1 -0
- package/build/adapters/hono/handlers/stream.d.ts +12 -0
- package/build/adapters/hono/handlers/stream.js +89 -0
- package/build/adapters/hono/handlers/stream.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/stream.test.js +3 -2
- package/build/adapters/hono/handlers/stream.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/index.d.ts +24 -12
- package/build/{implementations/http → adapters}/hono/index.js +19 -8
- package/build/adapters/hono/index.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/index.test.js +2 -4
- package/build/adapters/hono/index.test.js.map +1 -0
- package/build/{implementations/http → adapters/hono}/on-request-error.test.js +2 -2
- package/build/adapters/hono/on-request-error.test.js.map +1 -0
- package/build/adapters/hono/request.d.ts +7 -0
- package/build/adapters/hono/request.js +22 -0
- package/build/adapters/hono/request.js.map +1 -0
- package/build/{implementations/http → adapters/hono}/route-errors.test.js +4 -4
- package/build/adapters/hono/route-errors.test.js.map +1 -0
- package/build/adapters/hono/types.d.ts +55 -0
- package/build/adapters/hono/types.js +19 -0
- package/build/adapters/hono/types.js.map +1 -0
- package/build/client/freeze.test.js +39 -0
- package/build/client/freeze.test.js.map +1 -0
- package/build/client/typed-error-dispatch.test.js +2 -2
- package/build/client/typed-error-dispatch.test.js.map +1 -1
- package/build/codegen/__fixtures__/make-envelope.d.ts +1 -1
- package/build/codegen/bin/cli.d.ts +5 -0
- package/build/codegen/bin/cli.js +139 -182
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/bin/cli.test.js +12 -2
- package/build/codegen/bin/cli.test.js.map +1 -1
- package/build/codegen/bin/flag-specs.d.ts +9 -0
- package/build/codegen/bin/flag-specs.js +33 -31
- package/build/codegen/bin/flag-specs.js.map +1 -1
- package/build/codegen/bin/flag-specs.test.js +14 -1
- package/build/codegen/bin/flag-specs.test.js.map +1 -1
- package/build/codegen/collect-models.d.ts +1 -1
- package/build/codegen/emit/api-route.d.ts +8 -0
- package/build/codegen/emit/api-route.js +156 -0
- package/build/codegen/emit/api-route.js.map +1 -0
- package/build/codegen/emit/context.d.ts +30 -0
- package/build/codegen/emit/context.js +2 -0
- package/build/codegen/emit/context.js.map +1 -0
- package/build/codegen/emit/declarations.d.ts +24 -0
- package/build/codegen/emit/declarations.js +48 -0
- package/build/codegen/emit/declarations.js.map +1 -0
- package/build/codegen/emit/format-types.d.ts +61 -0
- package/build/codegen/emit/format-types.js +188 -0
- package/build/codegen/emit/format-types.js.map +1 -0
- package/build/codegen/emit/http-stream-route.d.ts +7 -0
- package/build/codegen/emit/http-stream-route.js +138 -0
- package/build/codegen/emit/http-stream-route.js.map +1 -0
- package/build/codegen/emit/route-shared.d.ts +35 -0
- package/build/codegen/emit/route-shared.js +88 -0
- package/build/codegen/emit/route-shared.js.map +1 -0
- package/build/codegen/emit/rpc-route.d.ts +7 -0
- package/build/codegen/emit/rpc-route.js +37 -0
- package/build/codegen/emit/rpc-route.js.map +1 -0
- package/build/codegen/emit/scope-file.d.ts +39 -0
- package/build/codegen/emit/scope-file.js +166 -0
- package/build/codegen/emit/scope-file.js.map +1 -0
- package/build/codegen/emit/stream-route.d.ts +7 -0
- package/build/codegen/emit/stream-route.js +62 -0
- package/build/codegen/emit/stream-route.js.map +1 -0
- package/build/codegen/emit-errors.d.ts +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-index.js +13 -0
- package/build/codegen/emit-index.js.map +1 -1
- package/build/codegen/emit-index.test.js +25 -0
- package/build/codegen/emit-index.test.js.map +1 -1
- package/build/codegen/emit-scope.d.ts +13 -30
- package/build/codegen/emit-scope.js +15 -807
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +86 -4
- package/build/codegen/emit-scope.test.js.map +1 -1
- package/build/codegen/goldens.test.js +69 -0
- package/build/codegen/goldens.test.js.map +1 -0
- package/build/codegen/group-routes.d.ts +1 -1
- package/build/codegen/pipeline.d.ts +1 -1
- package/build/codegen/resolve-envelope.d.ts +1 -1
- package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
- package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
- package/build/codegen/targets/_shared/target-run.d.ts +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
- package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
- package/build/core/create-http-stream.d.ts +50 -0
- package/build/core/create-http-stream.js +108 -0
- package/build/core/create-http-stream.js.map +1 -0
- package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
- package/build/core/create-http-stream.test.js.map +1 -0
- package/build/core/create-http.d.ts +51 -0
- package/build/core/create-http.js +65 -0
- package/build/core/create-http.js.map +1 -0
- package/build/{create-http.test.js → core/create-http.test.js} +13 -4
- package/build/core/create-http.test.js.map +1 -0
- package/build/core/create-stream.d.ts +26 -0
- package/build/core/create-stream.js +80 -0
- package/build/core/create-stream.js.map +1 -0
- package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
- package/build/core/create-stream.test.js.map +1 -0
- package/build/core/create.d.ts +22 -0
- package/build/core/create.js +71 -0
- package/build/core/create.js.map +1 -0
- package/build/{create.test.js → core/create.test.js} +25 -46
- package/build/core/create.test.js.map +1 -0
- package/build/core/definition-site.d.ts +24 -0
- package/build/{stack-utils.js → core/definition-site.js} +20 -20
- package/build/core/definition-site.js.map +1 -0
- package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
- package/build/core/definition-site.test.js.map +1 -0
- package/build/{errors.d.ts → core/errors.d.ts} +19 -8
- package/build/{errors.js → core/errors.js} +21 -26
- package/build/core/errors.js.map +1 -0
- package/build/core/errors.test.js.map +1 -0
- package/build/core/factory-options.test.js +82 -0
- package/build/core/factory-options.test.js.map +1 -0
- package/build/core/http-route.d.ts +13 -0
- package/build/core/http-route.js +54 -0
- package/build/core/http-route.js.map +1 -0
- package/build/core/internal.d.ts +72 -0
- package/build/core/internal.js +128 -0
- package/build/core/internal.js.map +1 -0
- package/build/{migration.test.js → core/migration.test.js} +17 -1
- package/build/core/migration.test.js.map +1 -0
- package/build/core/procedures.d.ts +143 -0
- package/build/core/procedures.js +64 -0
- package/build/core/procedures.js.map +1 -0
- package/build/{index.test.js → core/procedures.test.js} +14 -11
- package/build/core/procedures.test.js.map +1 -0
- package/build/core/types.d.ts +182 -0
- package/build/{schema → core}/types.js.map +1 -1
- package/build/exports.d.ts +31 -11
- package/build/exports.js +23 -8
- package/build/exports.js.map +1 -1
- package/build/schema/adapter.d.ts +35 -0
- package/build/schema/adapter.js +13 -0
- package/build/schema/adapter.js.map +1 -0
- package/build/schema/adapter.test.js +53 -0
- package/build/schema/adapter.test.js.map +1 -0
- package/build/schema/compile.d.ts +37 -0
- package/build/schema/compile.js +38 -0
- package/build/schema/compile.js.map +1 -0
- package/build/schema/compile.test.js +78 -0
- package/build/schema/compile.test.js.map +1 -0
- package/build/schema/compute-schema.d.ts +47 -37
- package/build/schema/compute-schema.js +86 -29
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/compute-schema.test.js +158 -40
- package/build/schema/compute-schema.test.js.map +1 -1
- package/build/schema/json-schema.d.ts +17 -0
- package/build/schema/json-schema.js +2 -0
- package/build/schema/json-schema.js.map +1 -0
- package/build/schema/typebox.d.ts +11 -0
- package/build/schema/typebox.js +24 -0
- package/build/schema/typebox.js.map +1 -0
- package/build/schema/typebox.test.js +34 -0
- package/build/schema/typebox.test.js.map +1 -0
- package/build/server/context.d.ts +8 -0
- package/build/server/context.js +7 -0
- package/build/server/context.js.map +1 -0
- package/build/server/context.test.js +16 -0
- package/build/server/context.test.js.map +1 -0
- package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
- package/build/server/doc-envelope.js.map +1 -0
- package/build/server/doc-envelope.test.d.ts +1 -0
- package/build/server/doc-envelope.test.js.map +1 -0
- package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
- package/build/{implementations/http → server}/doc-registry.js +9 -5
- package/build/server/doc-registry.js.map +1 -0
- package/build/server/doc-registry.test.d.ts +1 -0
- package/build/{implementations/http → server}/doc-registry.test.js +27 -24
- package/build/server/doc-registry.test.js.map +1 -0
- package/build/server/docs/docs.test.d.ts +1 -0
- package/build/server/docs/docs.test.js +237 -0
- package/build/server/docs/docs.test.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
- package/build/server/docs/http-doc.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
- package/build/server/docs/http-stream-doc.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
- package/build/server/docs/rpc-doc.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
- package/build/server/docs/stream-doc.js.map +1 -0
- package/build/server/errors/dispatch.d.ts +96 -0
- package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
- package/build/server/errors/dispatch.js.map +1 -0
- package/build/server/errors/dispatch.test.d.ts +1 -0
- package/build/server/errors/dispatch.test.js +418 -0
- package/build/server/errors/dispatch.test.js.map +1 -0
- package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
- package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
- package/build/server/errors/taxonomy.js.map +1 -0
- package/build/server/errors/taxonomy.test.d.ts +1 -0
- package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
- package/build/server/errors/taxonomy.test.js.map +1 -0
- package/build/server/index.d.ts +29 -0
- package/build/server/index.js +27 -0
- package/build/server/index.js.map +1 -0
- package/build/server/no-framework-imports.test.d.ts +1 -0
- package/build/server/no-framework-imports.test.js +40 -0
- package/build/server/no-framework-imports.test.js.map +1 -0
- package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
- package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
- package/build/server/paths.js.map +1 -0
- package/build/server/paths.test.d.ts +1 -0
- package/build/server/paths.test.js +111 -0
- package/build/server/paths.test.js.map +1 -0
- package/build/server/request/params.d.ts +29 -0
- package/build/server/request/params.js +43 -0
- package/build/server/request/params.js.map +1 -0
- package/build/server/request/params.test.d.ts +1 -0
- package/build/server/request/params.test.js +91 -0
- package/build/server/request/params.test.js.map +1 -0
- package/build/server/request/query.d.ts +9 -0
- package/build/server/request/query.js +22 -0
- package/build/server/request/query.js.map +1 -0
- package/build/server/request/query.test.d.ts +1 -0
- package/build/server/request/query.test.js +60 -0
- package/build/server/request/query.test.js.map +1 -0
- package/build/server/sse.d.ts +70 -0
- package/build/server/sse.js +94 -0
- package/build/server/sse.js.map +1 -0
- package/build/server/sse.test.d.ts +1 -0
- package/build/server/sse.test.js +98 -0
- package/build/server/sse.test.js.map +1 -0
- package/build/{implementations → server}/types.d.ts +17 -15
- package/build/{implementations → server}/types.js.map +1 -1
- package/docs/astro-adapter.md +8 -9
- package/docs/client-and-codegen.md +4 -4
- package/docs/client-error-handling.md +92 -5
- package/docs/codegen-kotlin.md +2 -3
- package/docs/codegen-swift.md +1 -2
- package/docs/core.md +135 -54
- package/docs/http-integrations.md +83 -6
- package/docs/migration-v8-to-v9.md +192 -0
- package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
- package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
- package/docs/streaming.md +12 -0
- package/package.json +23 -47
- package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
- package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
- package/src/adapters/hono/envelope-parity.test.ts +126 -0
- package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
- package/src/adapters/hono/handlers/http-stream.ts +73 -0
- package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
- package/src/adapters/hono/handlers/http.ts +70 -0
- package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
- package/src/adapters/hono/handlers/rpc.ts +39 -0
- package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
- package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
- package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
- package/src/{implementations/http → adapters}/hono/index.ts +35 -30
- package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
- package/src/adapters/hono/request.ts +28 -0
- package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
- package/src/{implementations/http → adapters}/hono/types.ts +43 -20
- package/src/client/freeze.test.ts +41 -0
- package/src/client/typed-error-dispatch.test.ts +3 -3
- package/src/codegen/__fixtures__/make-envelope.ts +1 -1
- package/src/codegen/__fixtures__/models-envelope.json +310 -0
- package/src/codegen/__fixtures__/users-envelope.json +9 -0
- package/src/codegen/__goldens__/MANIFEST.json +85 -0
- package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
- package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
- package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
- package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
- package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
- package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
- package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
- package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
- package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
- package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
- package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
- package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
- package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
- package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
- package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
- package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
- package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
- package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
- package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
- package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
- package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
- package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
- package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
- package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
- package/src/codegen/bin/cli.test.ts +13 -2
- package/src/codegen/bin/cli.ts +181 -144
- package/src/codegen/bin/flag-specs.test.ts +16 -1
- package/src/codegen/bin/flag-specs.ts +43 -31
- package/src/codegen/bundle-size.test.ts +1 -1
- package/src/codegen/collect-models.ts +1 -1
- package/src/codegen/e2e.test.ts +1 -1
- package/src/codegen/emit/api-route.ts +184 -0
- package/src/codegen/emit/context.ts +32 -0
- package/src/codegen/emit/declarations.ts +49 -0
- package/src/codegen/emit/format-types.ts +232 -0
- package/src/codegen/emit/http-stream-route.ts +162 -0
- package/src/codegen/emit/route-shared.ts +102 -0
- package/src/codegen/emit/rpc-route.ts +49 -0
- package/src/codegen/emit/scope-file.ts +226 -0
- package/src/codegen/emit/stream-route.ts +81 -0
- package/src/codegen/emit-errors.integration.test.ts +2 -2
- package/src/codegen/emit-errors.test.ts +1 -1
- package/src/codegen/emit-errors.ts +1 -1
- package/src/codegen/emit-index.test.ts +34 -0
- package/src/codegen/emit-index.ts +19 -0
- package/src/codegen/emit-scope.test.ts +96 -6
- package/src/codegen/emit-scope.ts +15 -1003
- package/src/codegen/goldens.test.ts +89 -0
- package/src/codegen/group-routes.test.ts +1 -1
- package/src/codegen/group-routes.ts +1 -1
- package/src/codegen/pipeline.test.ts +1 -1
- package/src/codegen/pipeline.ts +1 -1
- package/src/codegen/resolve-envelope.test.ts +1 -1
- package/src/codegen/resolve-envelope.ts +1 -1
- package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
- package/src/codegen/targets/_shared/error-schemas.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.ts +1 -1
- package/src/codegen/targets/_shared/target-run.ts +1 -1
- package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +6 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
- package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
- package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
- package/src/codegen/targets/swift/__fixtures__/users-golden.swift +6 -0
- package/src/codegen/targets/swift/access-level.test.ts +1 -1
- package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
- package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
- package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
- package/src/codegen/targets/ts/shared-models.test.ts +1 -1
- package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
- package/src/core/create-http-stream.ts +207 -0
- package/src/{create-http.test.ts → core/create-http.test.ts} +15 -4
- package/src/core/create-http.ts +126 -0
- package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
- package/src/core/create-stream.ts +142 -0
- package/src/{create.test.ts → core/create.test.ts} +25 -57
- package/src/core/create.ts +121 -0
- package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
- package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
- package/src/{errors.test.ts → core/errors.test.ts} +1 -1
- package/src/{errors.ts → core/errors.ts} +30 -28
- package/src/core/factory-options.test.ts +112 -0
- package/src/core/http-route.ts +73 -0
- package/src/core/internal.ts +203 -0
- package/src/{migration.test.ts → core/migration.test.ts} +23 -1
- package/src/{index.test.ts → core/procedures.test.ts} +13 -11
- package/src/core/procedures.ts +75 -0
- package/src/core/types.ts +195 -0
- package/src/exports.ts +60 -11
- package/src/schema/adapter.test.ts +58 -0
- package/src/schema/adapter.ts +45 -0
- package/src/schema/compile.test.ts +95 -0
- package/src/schema/compile.ts +64 -0
- package/src/schema/compute-schema.test.ts +222 -41
- package/src/schema/compute-schema.ts +145 -71
- package/src/schema/json-schema.ts +21 -0
- package/src/schema/typebox.test.ts +40 -0
- package/src/schema/typebox.ts +27 -0
- package/src/server/context.test.ts +22 -0
- package/src/server/context.ts +18 -0
- package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
- package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
- package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
- package/src/{implementations/http → server}/doc-registry.ts +11 -7
- package/src/server/docs/docs.test.ts +287 -0
- package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
- package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
- package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
- package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
- package/src/server/errors/dispatch.test.ts +450 -0
- package/src/server/errors/dispatch.ts +189 -0
- package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
- package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
- package/src/server/index.ts +29 -0
- package/src/server/no-framework-imports.test.ts +43 -0
- package/src/server/paths.test.ts +141 -0
- package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
- package/src/server/request/params.test.ts +143 -0
- package/src/server/request/params.ts +68 -0
- package/src/server/request/query.test.ts +70 -0
- package/src/server/request/query.ts +24 -0
- package/src/server/sse.test.ts +113 -0
- package/src/server/sse.ts +117 -0
- package/src/{implementations → server}/types.ts +17 -16
- package/build/create-http-stream.d.ts +0 -58
- package/build/create-http-stream.js +0 -122
- package/build/create-http-stream.js.map +0 -1
- package/build/create-http-stream.test.js.map +0 -1
- package/build/create-http.d.ts +0 -49
- package/build/create-http.js +0 -108
- package/build/create-http.js.map +0 -1
- package/build/create-http.test.js.map +0 -1
- package/build/create-stream.d.ts +0 -35
- package/build/create-stream.js +0 -123
- package/build/create-stream.js.map +0 -1
- package/build/create-stream.test.js.map +0 -1
- package/build/create.d.ts +0 -28
- package/build/create.js +0 -82
- package/build/create.js.map +0 -1
- package/build/create.test.js.map +0 -1
- package/build/doc-envelope.js.map +0 -1
- package/build/doc-envelope.test.js.map +0 -1
- package/build/errors.js.map +0 -1
- package/build/errors.test.js.map +0 -1
- package/build/implementations/http/astro/astro-context.js.map +0 -1
- package/build/implementations/http/astro/create-handler.js.map +0 -1
- package/build/implementations/http/astro/index.js.map +0 -1
- package/build/implementations/http/astro/index.test.js.map +0 -1
- package/build/implementations/http/astro/rewrite-request.js.map +0 -1
- package/build/implementations/http/doc-registry.js.map +0 -1
- package/build/implementations/http/doc-registry.test.js.map +0 -1
- package/build/implementations/http/error-dispatch.d.ts +0 -76
- package/build/implementations/http/error-dispatch.js.map +0 -1
- package/build/implementations/http/error-dispatch.test.js +0 -254
- package/build/implementations/http/error-dispatch.test.js.map +0 -1
- package/build/implementations/http/error-taxonomy.js.map +0 -1
- package/build/implementations/http/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
- package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
- package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
- package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
- package/build/implementations/http/hono/handlers/http-stream.js +0 -123
- package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
- package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
- package/build/implementations/http/hono/handlers/http.js +0 -110
- package/build/implementations/http/hono/handlers/http.js.map +0 -1
- package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
- package/build/implementations/http/hono/handlers/rpc.js +0 -32
- package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
- package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
- package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
- package/build/implementations/http/hono/handlers/stream.js +0 -147
- package/build/implementations/http/hono/handlers/stream.js.map +0 -1
- package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
- package/build/implementations/http/hono/index.js.map +0 -1
- package/build/implementations/http/hono/index.test.js.map +0 -1
- package/build/implementations/http/hono/path.js.map +0 -1
- package/build/implementations/http/hono/path.test.js +0 -83
- package/build/implementations/http/hono/path.test.js.map +0 -1
- package/build/implementations/http/hono/types.d.ts +0 -51
- package/build/implementations/http/hono/types.js.map +0 -1
- package/build/implementations/http/on-request-error.test.js.map +0 -1
- package/build/implementations/http/route-errors.test.js.map +0 -1
- package/build/index.d.ts +0 -175
- package/build/index.js +0 -47
- package/build/index.js.map +0 -1
- package/build/index.test.js.map +0 -1
- package/build/migration.test.js.map +0 -1
- package/build/schema/extract-json-schema.d.ts +0 -2
- package/build/schema/extract-json-schema.js +0 -12
- package/build/schema/extract-json-schema.js.map +0 -1
- package/build/schema/extract-json-schema.test.js +0 -23
- package/build/schema/extract-json-schema.test.js.map +0 -1
- package/build/schema/parser.d.ts +0 -36
- package/build/schema/parser.js +0 -210
- package/build/schema/parser.js.map +0 -1
- package/build/schema/parser.test.js +0 -120
- package/build/schema/parser.test.js.map +0 -1
- package/build/schema/resolve-schema-lib.d.ts +0 -12
- package/build/schema/resolve-schema-lib.js +0 -11
- package/build/schema/resolve-schema-lib.js.map +0 -1
- package/build/schema/resolve-schema-lib.test.js +0 -17
- package/build/schema/resolve-schema-lib.test.js.map +0 -1
- package/build/schema/types.d.ts +0 -8
- package/build/schema/types.js +0 -2
- package/build/stack-utils.d.ts +0 -25
- package/build/stack-utils.js.map +0 -1
- package/build/stack-utils.test.js.map +0 -1
- package/build/types.d.ts +0 -142
- package/build/types.js +0 -2
- package/build/types.js.map +0 -1
- package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
- package/docs/handoffs/ajsc-named-type-collision.md +0 -134
- package/docs/handoffs/ajsc-named-type-support.md +0 -181
- package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
- package/docs/npm-workspaces-migration-plan.md +0 -611
- package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
- package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
- package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
- package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
- package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
- package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
- package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
- package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
- package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
- package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
- package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
- package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
- package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
- package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
- package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
- package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
- package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
- package/src/create-http-stream.ts +0 -191
- package/src/create-http.ts +0 -210
- package/src/create-stream.ts +0 -228
- package/src/create.ts +0 -172
- package/src/implementations/http/README.md +0 -390
- package/src/implementations/http/error-dispatch.test.ts +0 -283
- package/src/implementations/http/error-dispatch.ts +0 -176
- package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
- package/src/implementations/http/hono/handlers/http.ts +0 -145
- package/src/implementations/http/hono/handlers/rpc.ts +0 -54
- package/src/implementations/http/hono/path.test.ts +0 -96
- package/src/index.ts +0 -101
- package/src/schema/extract-json-schema.test.ts +0 -25
- package/src/schema/extract-json-schema.ts +0 -15
- package/src/schema/parser.test.ts +0 -182
- package/src/schema/parser.ts +0 -265
- package/src/schema/resolve-schema-lib.test.ts +0 -19
- package/src/schema/resolve-schema-lib.ts +0 -29
- package/src/schema/types.ts +0 -20
- package/src/types.ts +0 -133
- /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
- /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
- /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/index.js +0 -0
- /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
- /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
- /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
- /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
- /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
- /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
- /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
- /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
- /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
- /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
- /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
- /package/build/{errors.test.js → core/errors.test.js} +0 -0
- /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
- /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
- /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
- /package/build/{implementations/http/hono → core}/types.js +0 -0
- /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
- /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
- /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
- /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
- /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
- /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
- /package/build/{implementations → server}/types.js +0 -0
- /package/src/{implementations/http → adapters}/astro/README.md +0 -0
- /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
- /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
- /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
- /package/src/{implementations/http → adapters}/astro/rewrite-request.ts +0 -0
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
# HTTP Implementations
|
|
2
|
-
|
|
3
|
-
HTTP implementation builders for `ts-procedures` that create type-safe, versioned endpoints with automatic path generation, schema-based validation, and route documentation.
|
|
4
|
-
|
|
5
|
-
## Available Implementations
|
|
6
|
-
|
|
7
|
-
| Framework | Subpath | Description |
|
|
8
|
-
|-----------|---------|-------------|
|
|
9
|
-
| HonoAppBuilder | `ts-procedures/hono` | Unified Hono builder — dispatches RPC, RPC streams, REST, and REST streams from one `register()` call. Works on Bun, Deno, Cloudflare Workers, and Node.js. |
|
|
10
|
-
|
|
11
|
-
`HonoAppBuilder` is a unified builder: one builder, one `app`, one set of cross-cutting hooks. The procedure kind drives behavior — pick the creator that matches the shape you need.
|
|
12
|
-
|
|
13
|
-
### Procedure kinds and which creator to use
|
|
14
|
-
|
|
15
|
-
| Procedure kind | Created with | Config shape | Handler return | HTTP methods | Use case |
|
|
16
|
-
|----------------|--------------|--------------|----------------|--------------|----------|
|
|
17
|
-
| `rpc` | `Create()` | `RPCConfig` (`scope`, `version`) | `Promise<T>` | POST | Standard request/response |
|
|
18
|
-
| `rpc-stream` | `CreateStream()` | `RPCConfig` | `AsyncGenerator<T>` | GET, POST | RPC-style streaming (SSE / text) |
|
|
19
|
-
| `http` | `CreateHttp()` | `APIConfig` (`path`, `method`) | `Promise<T>` | GET, POST, PUT, DELETE, PATCH, HEAD | REST-style endpoint with `schema.req` channels |
|
|
20
|
-
| `http-stream` | `CreateHttpStream()` | `APIConfig` | `AsyncGenerator<T>` | GET, POST | REST-style streaming endpoint |
|
|
21
|
-
|
|
22
|
-
`HonoAppBuilder` accepts all four kinds from the same factory.
|
|
23
|
-
|
|
24
|
-
## Core Concepts
|
|
25
|
-
|
|
26
|
-
### Config Interfaces
|
|
27
|
-
|
|
28
|
-
RPC and RPC-stream procedures share `RPCConfig`:
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
interface RPCConfig {
|
|
32
|
-
scope: string | string[] // Route path segment(s)
|
|
33
|
-
version: number // API version number
|
|
34
|
-
errors?: string[] // Optional taxonomy keys for typed-client narrowing
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
REST-style procedures (`CreateHttp` / `CreateHttpStream`) use `APIConfig`:
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
interface APIConfig {
|
|
42
|
-
path: string // Route path with Hono params (e.g., '/users/:id')
|
|
43
|
-
method: HttpMethod // 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
|
|
44
|
-
successStatus?: number // Default: POST→201, DELETE→204, others→200
|
|
45
|
-
scope?: string // Optional grouping for codegen file emission
|
|
46
|
-
errors?: string[]
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
API routes use developer-defined paths — no auto-generation from scope/version.
|
|
51
|
-
|
|
52
|
-
### Path Generation
|
|
53
|
-
|
|
54
|
-
Routes are generated using kebab-case conversion:
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
/{pathPrefix}/{scope...}/{procedureName}/{version}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**Examples:**
|
|
61
|
-
|
|
62
|
-
| Scope | Procedure Name | Version | Generated Path |
|
|
63
|
-
|-------|----------------|---------|----------------|
|
|
64
|
-
| `'users'` | `'Create'` | `1` | `/users/create/1` |
|
|
65
|
-
| `'users'` | `'GetById'` | `1` | `/users/get-by-id/1` |
|
|
66
|
-
| `['users', 'admin']` | `'List'` | `1` | `/users/admin/list/1` |
|
|
67
|
-
| `['UserModule', 'permissions']` | `'Update'` | `2` | `/user-module/permissions/update/2` |
|
|
68
|
-
|
|
69
|
-
**With pathPrefix `/api/v1`:**
|
|
70
|
-
|
|
71
|
-
| Scope | Procedure Name | Version | Generated Path |
|
|
72
|
-
|-------|----------------|---------|----------------|
|
|
73
|
-
| `'users'` | `'Create'` | `1` | `/api/v1/users/create/1` |
|
|
74
|
-
| `['users', 'admin']` | `'Delete'` | `2` | `/api/v1/users/admin/delete/2` |
|
|
75
|
-
|
|
76
|
-
### Context Resolution
|
|
77
|
-
|
|
78
|
-
The `factoryContext` parameter supports three patterns:
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
// 1. Static object
|
|
82
|
-
builder.register(Factory, { userId: 'static-123' })
|
|
83
|
-
|
|
84
|
-
// 2. Sync function
|
|
85
|
-
builder.register(Factory, (c) => ({
|
|
86
|
-
userId: c.req.header('x-user-id')
|
|
87
|
-
}))
|
|
88
|
-
|
|
89
|
-
// 3. Async function
|
|
90
|
-
builder.register(Factory, async (c) => {
|
|
91
|
-
const user = await validateToken(c.req.header('authorization'))
|
|
92
|
-
return { userId: user.id }
|
|
93
|
-
})
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Abort Signal
|
|
97
|
-
|
|
98
|
-
All HTTP implementations automatically inject an `AbortSignal` into the handler context as `ctx.signal`. This signal aborts when the client disconnects, enabling handlers to cancel in-flight work (fetch calls, database queries, etc.).
|
|
99
|
-
|
|
100
|
-
| Framework | Procedure kind | Signal source | Behavior |
|
|
101
|
-
|-----------|---------------|---------------|----------|
|
|
102
|
-
| HonoAppBuilder | `rpc`, `http` | `c.req.raw.signal` | Web standard Request signal |
|
|
103
|
-
| HonoAppBuilder | `rpc-stream`, `http-stream` | `c.req.raw.signal` | Combined with internal stream AbortController via `AbortSignal.any()` |
|
|
104
|
-
|
|
105
|
-
For streaming procedures, `signal.reason` is `'stream-completed'` on normal completion, allowing handlers to distinguish from client disconnection.
|
|
106
|
-
|
|
107
|
-
### Error Handling
|
|
108
|
-
|
|
109
|
-
Both builders support two peer error-handling modes — **declarative** (`errors` taxonomy + `unknownError`) and **imperative** (`onError` callback) — plus a cross-cutting `onRequestError` observer for logging, tracing, and metrics.
|
|
110
|
-
|
|
111
|
-
Full spec (taxonomy shape, `toResponse`/`onCatch`/`match`, per-route narrowing, mid-stream caveats): **[docs/http-integrations.md § Error Handling](../../../docs/http-integrations.md#error-handling)**.
|
|
112
|
-
|
|
113
|
-
### Lifecycle Hooks
|
|
114
|
-
|
|
115
|
-
`HonoAppBuilder`'s config is **stratified**: cross-cutting hooks live at the top level, kind-specific hooks live inside `rpc:`, `api:`, and `stream:` blocks.
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
new HonoAppBuilder({
|
|
119
|
-
// Cross-cutting (every kind)
|
|
120
|
-
onRequestStart, onRequestEnd, onRequestError,
|
|
121
|
-
errors, unknownError, onError,
|
|
122
|
-
|
|
123
|
-
// Kind-specific blocks
|
|
124
|
-
rpc: { onSuccess },
|
|
125
|
-
api: { queryParser, onSuccess },
|
|
126
|
-
stream: { defaultStreamMode, onStreamStart, onStreamEnd, onMidStreamError },
|
|
127
|
-
})
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Non-streaming flow (`rpc`, `http`):**
|
|
131
|
-
|
|
132
|
-
```
|
|
133
|
-
onRequestStart → handler → onSuccess → onRequestEnd
|
|
134
|
-
↓
|
|
135
|
-
(on error)
|
|
136
|
-
↓
|
|
137
|
-
onRequestError (observer)
|
|
138
|
-
↓
|
|
139
|
-
errors taxonomy / onError fallback
|
|
140
|
-
↓
|
|
141
|
-
onRequestEnd
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
**Streaming flow (`rpc-stream`, `http-stream`):**
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
onRequestStart → onStreamStart → [yields...] → onStreamEnd → onRequestEnd
|
|
148
|
-
↓
|
|
149
|
-
(on error)
|
|
150
|
-
↓
|
|
151
|
-
┌──────────────────┴──────────────────┐
|
|
152
|
-
↓ (pre-stream — before onStreamStart) ↓ (mid-stream — after first yield)
|
|
153
|
-
errors taxonomy / onError → onRequestEnd onMidStreamError → onStreamEnd → onRequestEnd
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Pre-stream errors short-circuit before `onStreamStart` fires, so `onStreamEnd` does **not** run on that path. Mid-stream errors run `onMidStreamError` and then still fire `onStreamEnd` because the stream had already started.
|
|
157
|
-
|
|
158
|
-
| Hook | Config block | Triggers on |
|
|
159
|
-
|------|--------------|-------------|
|
|
160
|
-
| `onRequestStart` | top-level | Before route handler (every kind) |
|
|
161
|
-
| `onRequestEnd` | top-level | After response sent (every kind) |
|
|
162
|
-
| `onRequestError` | top-level | Cross-cutting observer — fires for every caught error before dispatch |
|
|
163
|
-
| `onError` | top-level | Imperative pre-stream error callback — peer of `errors` taxonomy |
|
|
164
|
-
| `errors` / `unknownError` | top-level | Declarative taxonomy dispatch |
|
|
165
|
-
| `rpc.onSuccess` | `rpc:` | After successful RPC handler |
|
|
166
|
-
| `api.onSuccess` | `api:` | After successful HTTP handler |
|
|
167
|
-
| `api.queryParser` | `api:` | Custom query-string parser for HTTP routes |
|
|
168
|
-
| `stream.defaultStreamMode` | `stream:` | Default mode (`'sse'` \| `'text'`) for both stream kinds |
|
|
169
|
-
| `stream.onStreamStart` | `stream:` | Before first yield |
|
|
170
|
-
| `stream.onStreamEnd` | `stream:` | After stream completes |
|
|
171
|
-
| `stream.onMidStreamError` | `stream:` | On mid-stream error (generator throws after first yield) |
|
|
172
|
-
|
|
173
|
-
### Route Documentation
|
|
174
|
-
|
|
175
|
-
Each registered procedure generates documentation accessible via `builder.docs`. Every doc carries a `kind` discriminant for reliable narrowing.
|
|
176
|
-
|
|
177
|
-
**RPC (`RPCHttpRouteDoc`, `kind: 'rpc'`):**
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
interface RPCHttpRouteDoc {
|
|
181
|
-
kind: 'rpc'
|
|
182
|
-
name: string
|
|
183
|
-
path: string
|
|
184
|
-
method: 'post'
|
|
185
|
-
scope: string | string[]
|
|
186
|
-
version: number
|
|
187
|
-
errors?: string[]
|
|
188
|
-
jsonSchema: {
|
|
189
|
-
body?: object // From schema.params
|
|
190
|
-
response?: object // From schema.returnType
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**RPC stream (`StreamHttpRouteDoc`, `kind: 'stream'`):**
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
interface StreamHttpRouteDoc {
|
|
199
|
-
kind: 'stream'
|
|
200
|
-
name: string
|
|
201
|
-
path: string
|
|
202
|
-
methods: ('get' | 'post')[]
|
|
203
|
-
streamMode: 'sse' | 'text'
|
|
204
|
-
scope: string | string[]
|
|
205
|
-
version: number
|
|
206
|
-
errors?: string[]
|
|
207
|
-
jsonSchema: {
|
|
208
|
-
params?: object // From schema.params
|
|
209
|
-
yieldType?: object // From schema.yieldType
|
|
210
|
-
returnType?: object // From schema.returnType
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**REST (`APIHttpRouteDoc`, `kind: 'api'`):**
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
interface APIHttpRouteDoc {
|
|
219
|
-
kind: 'api'
|
|
220
|
-
name: string
|
|
221
|
-
scope?: string
|
|
222
|
-
path: string
|
|
223
|
-
method: HttpMethod
|
|
224
|
-
fullPath: string // Includes pathPrefix
|
|
225
|
-
successStatus?: number
|
|
226
|
-
errors?: string[]
|
|
227
|
-
jsonSchema: {
|
|
228
|
-
req?: {
|
|
229
|
-
pathParams?: object // From schema.req.pathParams
|
|
230
|
-
query?: object // From schema.req.query
|
|
231
|
-
body?: object // From schema.req.body
|
|
232
|
-
headers?: object // From schema.req.headers
|
|
233
|
-
}
|
|
234
|
-
res?: {
|
|
235
|
-
body?: object // From schema.res.body
|
|
236
|
-
headers?: object // From schema.res.headers
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
**REST stream (`HttpStreamRouteDoc`, `kind: 'http-stream'`):**
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
interface HttpStreamRouteDoc {
|
|
246
|
-
kind: 'http-stream'
|
|
247
|
-
name: string
|
|
248
|
-
scope?: string
|
|
249
|
-
path: string
|
|
250
|
-
method: HttpMethod
|
|
251
|
-
fullPath: string
|
|
252
|
-
streamMode: 'sse' | 'text'
|
|
253
|
-
errors?: string[]
|
|
254
|
-
jsonSchema: {
|
|
255
|
-
req?: {
|
|
256
|
-
pathParams?: object
|
|
257
|
-
query?: object
|
|
258
|
-
body?: object
|
|
259
|
-
headers?: object
|
|
260
|
-
}
|
|
261
|
-
res?: { headers?: object }
|
|
262
|
-
yield?: object
|
|
263
|
-
returnType?: object
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Builder Pattern
|
|
269
|
-
|
|
270
|
-
Both builders follow the same pattern:
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
const builder = new HonoAppBuilder(config)
|
|
274
|
-
.register(PublicFactory, publicContextResolver)
|
|
275
|
-
.register(ProtectedFactory, protectedContextResolver)
|
|
276
|
-
|
|
277
|
-
const app = builder.build()
|
|
278
|
-
const docs = builder.docs
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
`build()` is synchronous — it returns the framework app instance directly. Don't `await` the call.
|
|
282
|
-
|
|
283
|
-
A single `HonoAppBuilder` registers procedures of every kind from the same factory and mounts them on one Hono `app`. The optional `app?: Hono` config lets you mount onto an existing Hono instance that already has custom middleware, health checks, or static routes; if omitted, the builder constructs its own.
|
|
284
|
-
|
|
285
|
-
**Key methods:**
|
|
286
|
-
|
|
287
|
-
| Method | Returns | Description |
|
|
288
|
-
|--------|---------|-------------|
|
|
289
|
-
| `register(factory, context, options?)` | `this` | Register a procedure factory |
|
|
290
|
-
| `build()` | Framework app | Create routes and return the application |
|
|
291
|
-
|
|
292
|
-
**Properties:**
|
|
293
|
-
|
|
294
|
-
| Property | Type | Description |
|
|
295
|
-
|----------|------|-------------|
|
|
296
|
-
| `app` | Framework app | The underlying framework application |
|
|
297
|
-
| `docs` | Route doc array | Route documentation, lazily computed on first read; `build()` also populates it. |
|
|
298
|
-
|
|
299
|
-
### DocRegistry
|
|
300
|
-
|
|
301
|
-
For single-builder apps, prefer `builder.toDocEnvelope({ basePath, errors })` — it wraps `DocRegistry` for the common case and produces an envelope identical to the multi-app aggregator.
|
|
302
|
-
|
|
303
|
-
For multi-app aggregation (e.g., two `HonoAppBuilder` instances mounted on different prefixes), `DocRegistry` composes route documentation from every source into one typed envelope:
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
307
|
-
|
|
308
|
-
const docs = new DocRegistry({
|
|
309
|
-
basePath: '/api',
|
|
310
|
-
errors: appErrors, // your ErrorTaxonomy — framework defaults auto-merged and deduped
|
|
311
|
-
})
|
|
312
|
-
.from(builderA)
|
|
313
|
-
.from(builderB)
|
|
314
|
-
|
|
315
|
-
app.get('/docs', (c) => c.json(docs.toJSON()))
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
- `from()` stores a reference — routes are read lazily at `toJSON()` time
|
|
319
|
-
- `toJSON()` supports optional `filter` and `transform` options
|
|
320
|
-
- `errors` accepts an `ErrorTaxonomy` or raw `ErrorDoc[]`; framework defaults are auto-merged (opt out via `includeDefaults: false`)
|
|
321
|
-
- Every builder satisfies the `DocSource` interface (`{ readonly docs: AnyHttpRouteDoc[] }`)
|
|
322
|
-
|
|
323
|
-
### Client Code Generation
|
|
324
|
-
|
|
325
|
-
Generate type-safe client SDKs from your DocRegistry output. The codegen reads the `DocEnvelope` JSON and produces per-scope TypeScript files with typed params, response types, and callable functions.
|
|
326
|
-
|
|
327
|
-
**Requirements:** every route needs a `scope` field for file grouping. RPC and stream routes carry `scope` via `RPCConfig`. REST routes (`CreateHttp` / `CreateHttpStream`) carry an optional `scope` directly on the procedure config:
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
procs.CreateHttp('GetUser', {
|
|
331
|
-
path: '/users/:id',
|
|
332
|
-
method: 'get',
|
|
333
|
-
scope: 'users', // Used for codegen file grouping
|
|
334
|
-
schema: { req: { pathParams: Type.Object({ id: Type.String() }) } },
|
|
335
|
-
}, async (_, { pathParams }) => /* ... */)
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
**Route-doc kinds and how the codegen consumes them:**
|
|
339
|
-
|
|
340
|
-
| Doc kind | Source procedure | Params shape in generated callable | Generated callable |
|
|
341
|
-
|----------|------------------|------------------------------------|--------------------|
|
|
342
|
-
| `rpc` | `Create()` | `jsonSchema.body` → flat params | `client.call()` |
|
|
343
|
-
| `api` | `CreateHttp()` | `jsonSchema.req.{pathParams,query,body,headers}` → structured params | `client.call()` |
|
|
344
|
-
| `stream` | `CreateStream()` | `jsonSchema.params` → flat params | `client.stream()` |
|
|
345
|
-
| `http-stream` | `CreateHttpStream()` | `jsonSchema.req.{pathParams,query,body,headers}` → structured params | `client.stream()` |
|
|
346
|
-
|
|
347
|
-
**SSE return values:** Stream procedures that return a value have it sent as `event: 'return'` SSE message. The client's `TypedStream.result` promise resolves with this value.
|
|
348
|
-
|
|
349
|
-
**SSE yieldType unwrapping:** Codegen detects the SSE envelope in `yieldType` and uses the inner `data` schema for the generated `Yield` type.
|
|
350
|
-
|
|
351
|
-
**Kind discriminant:** All route docs include a `kind` field (`'rpc' | 'api' | 'stream' | 'http-stream'`) for reliable type narrowing in the codegen pipeline and consumer code.
|
|
352
|
-
|
|
353
|
-
**Namespace types:** Use `--namespace-types` to wrap generated types in nested TS namespaces (`Scope.Route.Params`) instead of flat prefixed exports (`RouteParams`).
|
|
354
|
-
|
|
355
|
-
**ajsc options:** `--enum-style`, `--depluralize`, `--array-item-naming`, `--uncountable-words` are passed through to the ajsc TypescriptConverter for JSON Schema → TypeScript conversion.
|
|
356
|
-
|
|
357
|
-
**Self-contained mode:** Use `--self-contained` to emit `_types.ts` (all client type definitions) and `_client.ts` (runtime: `createClient`, `createFetchAdapter`, hooks, errors) into the output directory. All scope files and `index.ts` will import from `./_types` instead of `ts-procedures/client`, so consumers have no runtime dependency on `ts-procedures`. With this flag, `ts-procedures` can be a devDependency only.
|
|
358
|
-
|
|
359
|
-
**Clean output directory:** Use `--clean-out-dir` (or `cleanOutDir: true` in the programmatic API) to recursively remove the output directory before writing, so scope files left over from deleted or renamed scopes are pruned. Skipped under `--dry-run`.
|
|
360
|
-
|
|
361
|
-
## TypeScript Types
|
|
362
|
-
|
|
363
|
-
```typescript
|
|
364
|
-
import type {
|
|
365
|
-
RPCConfig,
|
|
366
|
-
RPCHttpRouteDoc,
|
|
367
|
-
StreamHttpRouteDoc,
|
|
368
|
-
HttpStreamRouteDoc,
|
|
369
|
-
StreamMode,
|
|
370
|
-
APIConfig,
|
|
371
|
-
APIHttpRouteDoc,
|
|
372
|
-
APIInput,
|
|
373
|
-
HttpMethod,
|
|
374
|
-
} from 'ts-procedures/http'
|
|
375
|
-
|
|
376
|
-
// Client Runtime
|
|
377
|
-
import { createClient, createFetchAdapter } from 'ts-procedures/client'
|
|
378
|
-
import type {
|
|
379
|
-
ClientAdapter,
|
|
380
|
-
ClientHooks,
|
|
381
|
-
TypedStream,
|
|
382
|
-
ClientInstance,
|
|
383
|
-
ProcedureCallDefaults,
|
|
384
|
-
ProcedureCallOptions,
|
|
385
|
-
RequestMeta,
|
|
386
|
-
} from 'ts-procedures/client'
|
|
387
|
-
|
|
388
|
-
// Code Generation (build-time only)
|
|
389
|
-
import { generateClient } from 'ts-procedures/codegen'
|
|
390
|
-
```
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, vi } from 'vitest'
|
|
2
|
-
import { Hono } from 'hono'
|
|
3
|
-
import { dispatchMidStreamError, dispatchPreStreamError } from './error-dispatch.js'
|
|
4
|
-
import { defineErrorTaxonomy } from './error-taxonomy.js'
|
|
5
|
-
import { ProcedureValidationError } from '../../errors.js'
|
|
6
|
-
|
|
7
|
-
function makeContext() {
|
|
8
|
-
return new Promise<import('hono').Context>((resolve) => {
|
|
9
|
-
const app = new Hono()
|
|
10
|
-
app.get('/', (c) => {
|
|
11
|
-
resolve(c)
|
|
12
|
-
return c.text('ok')
|
|
13
|
-
})
|
|
14
|
-
app.request('/')
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const dummyProcedure = {
|
|
19
|
-
name: 'Test',
|
|
20
|
-
kind: 'rpc',
|
|
21
|
-
config: { scope: 'test', version: 1 },
|
|
22
|
-
handler: async () => undefined,
|
|
23
|
-
} as any
|
|
24
|
-
|
|
25
|
-
describe('dispatchPreStreamError', () => {
|
|
26
|
-
test('default taxonomy: ProcedureValidationError → 400', async () => {
|
|
27
|
-
const c = await makeContext()
|
|
28
|
-
const err = new ProcedureValidationError('Test', 'Validation error for Test', [
|
|
29
|
-
{ instancePath: '/x', message: 'expected number' } as any,
|
|
30
|
-
])
|
|
31
|
-
|
|
32
|
-
const res = await dispatchPreStreamError({
|
|
33
|
-
err,
|
|
34
|
-
procedure: dummyProcedure,
|
|
35
|
-
raw: c,
|
|
36
|
-
cfg: {},
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
expect(res.status).toBe(400)
|
|
40
|
-
const body = await res.json()
|
|
41
|
-
expect(body.name).toBe('ProcedureValidationError')
|
|
42
|
-
expect(body.procedureName).toBe('Test')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
test('user taxonomy entry takes precedence', async () => {
|
|
46
|
-
const c = await makeContext()
|
|
47
|
-
class MyError extends Error {
|
|
48
|
-
constructor(public reason: string) { super(reason) }
|
|
49
|
-
}
|
|
50
|
-
const errors = defineErrorTaxonomy({
|
|
51
|
-
MyError: { class: MyError, statusCode: 422, toResponse: (e) => ({ name: 'MyError', reason: e.reason }) },
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const res = await dispatchPreStreamError({
|
|
55
|
-
err: new MyError('boom'),
|
|
56
|
-
procedure: dummyProcedure,
|
|
57
|
-
raw: c,
|
|
58
|
-
cfg: { errors },
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
expect(res.status).toBe(422)
|
|
62
|
-
expect(await res.json()).toEqual({ name: 'MyError', reason: 'boom' })
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('falls back to onError imperative callback', async () => {
|
|
66
|
-
const c = await makeContext()
|
|
67
|
-
const onError = vi.fn().mockResolvedValue(c.json({ ok: false }, 503))
|
|
68
|
-
|
|
69
|
-
const res = await dispatchPreStreamError({
|
|
70
|
-
err: new Error('unmatched'),
|
|
71
|
-
procedure: dummyProcedure,
|
|
72
|
-
raw: c,
|
|
73
|
-
cfg: { onError: (proc, raw, e) => onError(proc, raw, e) },
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
expect(onError).toHaveBeenCalled()
|
|
77
|
-
expect(res.status).toBe(503)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
test('hard default 500 when nothing matches', async () => {
|
|
81
|
-
const c = await makeContext()
|
|
82
|
-
const res = await dispatchPreStreamError({
|
|
83
|
-
err: new Error('boom'),
|
|
84
|
-
procedure: dummyProcedure,
|
|
85
|
-
raw: c,
|
|
86
|
-
cfg: {},
|
|
87
|
-
})
|
|
88
|
-
expect(res.status).toBe(500)
|
|
89
|
-
expect(await res.json()).toEqual({ error: 'boom' })
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
test('onRequestError observer fires before dispatch and is awaited', async () => {
|
|
93
|
-
const c = await makeContext()
|
|
94
|
-
const calls: string[] = []
|
|
95
|
-
const onRequestError = vi.fn(async () => {
|
|
96
|
-
calls.push('observer')
|
|
97
|
-
})
|
|
98
|
-
const errors = defineErrorTaxonomy({
|
|
99
|
-
AnyError: {
|
|
100
|
-
match: (e): e is Error => e instanceof Error,
|
|
101
|
-
statusCode: 500,
|
|
102
|
-
toResponse: () => {
|
|
103
|
-
calls.push('toResponse')
|
|
104
|
-
return { name: 'AnyError' }
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
await dispatchPreStreamError({
|
|
110
|
-
err: new Error('x'),
|
|
111
|
-
procedure: dummyProcedure,
|
|
112
|
-
raw: c,
|
|
113
|
-
cfg: { errors, onRequestError },
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
expect(calls).toEqual(['observer', 'toResponse'])
|
|
117
|
-
expect(onRequestError).toHaveBeenCalledWith({ err: expect.any(Error), procedure: dummyProcedure, raw: c })
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
test('observer throw is swallowed and logged', async () => {
|
|
121
|
-
const c = await makeContext()
|
|
122
|
-
const consoleErr = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
123
|
-
const onRequestError = vi.fn(() => { throw new Error('observer broke') })
|
|
124
|
-
|
|
125
|
-
const res = await dispatchPreStreamError({
|
|
126
|
-
err: new Error('x'),
|
|
127
|
-
procedure: dummyProcedure,
|
|
128
|
-
raw: c,
|
|
129
|
-
cfg: { onRequestError },
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
expect(res.status).toBe(500)
|
|
133
|
-
expect(consoleErr).toHaveBeenCalled()
|
|
134
|
-
consoleErr.mockRestore()
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
test('taxonomy onCatch is fully awaited before the response body is written', async () => {
|
|
138
|
-
const c = await makeContext()
|
|
139
|
-
const calls: string[] = []
|
|
140
|
-
class HookedError extends Error {}
|
|
141
|
-
const errors = defineErrorTaxonomy({
|
|
142
|
-
HookedError: {
|
|
143
|
-
class: HookedError,
|
|
144
|
-
statusCode: 500,
|
|
145
|
-
toResponse: () => ({ name: 'HookedError' }),
|
|
146
|
-
onCatch: async () => {
|
|
147
|
-
// Awaiting an async setTimeout here means a non-awaiting dispatcher
|
|
148
|
-
// would write the response BEFORE this push runs. The order we capture
|
|
149
|
-
// in `calls` reveals which branch happened.
|
|
150
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 5))
|
|
151
|
-
calls.push('oncatch-done')
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
// Spy on c.json: when the dispatcher writes the response, record where in
|
|
157
|
-
// the timeline it happened relative to onCatch's push.
|
|
158
|
-
const originalJson = c.json.bind(c) as (...args: any[]) => Response
|
|
159
|
-
const jsonSpy = vi.fn((...args: any[]) => {
|
|
160
|
-
calls.push('response-write')
|
|
161
|
-
return originalJson(...args)
|
|
162
|
-
})
|
|
163
|
-
;(c as any).json = jsonSpy
|
|
164
|
-
|
|
165
|
-
const res = await dispatchPreStreamError({
|
|
166
|
-
err: new HookedError('boom'),
|
|
167
|
-
procedure: dummyProcedure,
|
|
168
|
-
raw: c,
|
|
169
|
-
cfg: { errors },
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
expect(res.status).toBe(500)
|
|
173
|
-
expect(jsonSpy).toHaveBeenCalledTimes(1)
|
|
174
|
-
// If the dispatcher awaits onCatch first, 'oncatch-done' precedes
|
|
175
|
-
// 'response-write'. Without the await, the order would be reversed
|
|
176
|
-
// (or 'oncatch-done' would land after the dispatcher returns).
|
|
177
|
-
expect(calls).toEqual(['oncatch-done', 'response-write'])
|
|
178
|
-
})
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
const streamProcedure = {
|
|
182
|
-
name: 'Tail',
|
|
183
|
-
kind: 'rpc-stream',
|
|
184
|
-
config: { scope: 'logs', version: 1 },
|
|
185
|
-
handler: async function* () {},
|
|
186
|
-
} as any
|
|
187
|
-
|
|
188
|
-
describe('dispatchMidStreamError', () => {
|
|
189
|
-
test('uses taxonomy body when configured', async () => {
|
|
190
|
-
const c = await makeContext()
|
|
191
|
-
class MidErr extends Error {}
|
|
192
|
-
const errors = defineErrorTaxonomy({
|
|
193
|
-
MidErr: { class: MidErr, statusCode: 500, toResponse: () => ({ name: 'MidErr', detail: 'x' }) },
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
const result = await dispatchMidStreamError({
|
|
197
|
-
err: new MidErr('mid'),
|
|
198
|
-
procedure: streamProcedure,
|
|
199
|
-
raw: c,
|
|
200
|
-
cfg: { errors },
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
expect(result.data).toEqual({ name: 'MidErr', detail: 'x' })
|
|
204
|
-
expect(result.sseEvent).toBe('error')
|
|
205
|
-
expect(result.runOnCatch).toBeTypeOf('function')
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
test('falls back to onMidStreamError', async () => {
|
|
209
|
-
const c = await makeContext()
|
|
210
|
-
const result = await dispatchMidStreamError({
|
|
211
|
-
err: new Error('boom'),
|
|
212
|
-
procedure: streamProcedure,
|
|
213
|
-
raw: c,
|
|
214
|
-
cfg: {
|
|
215
|
-
onMidStreamError: () => ({ data: { kind: 'fail', msg: 'boom' } }),
|
|
216
|
-
},
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
expect(result.data).toEqual({ kind: 'fail', msg: 'boom' })
|
|
220
|
-
expect(result.sseEvent).toBe('Tail') // procedure name override
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
test('hard default { error: msg } when nothing matches', async () => {
|
|
224
|
-
const c = await makeContext()
|
|
225
|
-
const result = await dispatchMidStreamError({
|
|
226
|
-
err: new Error('plain'),
|
|
227
|
-
procedure: streamProcedure,
|
|
228
|
-
raw: c,
|
|
229
|
-
cfg: {},
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
expect(result.data).toEqual({ error: 'plain' })
|
|
233
|
-
expect(result.sseEvent).toBe('error')
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
test('onRequestError observer fires before dispatch (mid-stream form)', async () => {
|
|
237
|
-
const c = await makeContext()
|
|
238
|
-
const onRequestError = vi.fn(async () => {})
|
|
239
|
-
|
|
240
|
-
await dispatchMidStreamError({
|
|
241
|
-
err: new Error('x'),
|
|
242
|
-
procedure: streamProcedure,
|
|
243
|
-
raw: c,
|
|
244
|
-
cfg: { onRequestError },
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
expect(onRequestError).toHaveBeenCalled()
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
test('returns a runOnCatch function that is invokable and awaits the taxonomy hook', async () => {
|
|
251
|
-
const c = await makeContext()
|
|
252
|
-
const calls: string[] = []
|
|
253
|
-
class StreamErr extends Error {}
|
|
254
|
-
const errors = defineErrorTaxonomy({
|
|
255
|
-
StreamErr: {
|
|
256
|
-
class: StreamErr,
|
|
257
|
-
statusCode: 500,
|
|
258
|
-
toResponse: () => ({ name: 'StreamErr' }),
|
|
259
|
-
onCatch: async () => {
|
|
260
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 5))
|
|
261
|
-
calls.push('oncatch-done')
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// Mid-stream contract: the dispatcher does NOT call runOnCatch itself —
|
|
267
|
-
// the caller (stream handler) decides when to invoke it. Verify the shape
|
|
268
|
-
// and that awaiting it actually awaits the hook.
|
|
269
|
-
const result = await dispatchMidStreamError({
|
|
270
|
-
err: new StreamErr('mid'),
|
|
271
|
-
procedure: streamProcedure,
|
|
272
|
-
raw: c,
|
|
273
|
-
cfg: { errors },
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
expect(result.runOnCatch).toBeTypeOf('function')
|
|
277
|
-
// Hook has not yet run — caller hasn't invoked it.
|
|
278
|
-
expect(calls).toEqual([])
|
|
279
|
-
|
|
280
|
-
await result.runOnCatch!()
|
|
281
|
-
expect(calls).toEqual(['oncatch-done'])
|
|
282
|
-
})
|
|
283
|
-
})
|