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
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
RPCHttpRouteDoc,
|
|
4
4
|
APIHttpRouteDoc,
|
|
5
5
|
StreamHttpRouteDoc,
|
|
6
|
+
HttpStreamRouteDoc,
|
|
6
7
|
} from '../implementations/types.js'
|
|
7
8
|
import {
|
|
8
9
|
jsonSchemaToTypeString,
|
|
@@ -58,7 +59,8 @@ interface EmitRouteContext {
|
|
|
58
59
|
* This provides backward compatibility with servers running older ts-procedures
|
|
59
60
|
* versions that don't set `kind` on route docs.
|
|
60
61
|
*/
|
|
61
|
-
function inferRouteKind(route: Record<string, unknown>): 'rpc' | 'api' | 'stream' {
|
|
62
|
+
function inferRouteKind(route: Record<string, unknown>): 'rpc' | 'api' | 'stream' | 'http-stream' {
|
|
63
|
+
if ('streamMode' in route && 'fullPath' in route) return 'http-stream'
|
|
62
64
|
if ('streamMode' in route) return 'stream'
|
|
63
65
|
if ('fullPath' in route) return 'api'
|
|
64
66
|
return 'rpc'
|
|
@@ -291,92 +293,383 @@ async function emitRpcRoute(route: RPCHttpRouteDoc, ctx: EmitRouteContext): Prom
|
|
|
291
293
|
return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
|
|
292
294
|
}
|
|
293
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Formats a group of named types into a nested sub-namespace block (for namespace mode).
|
|
298
|
+
* Returns an array of lines to be inserted into the parent namespace, and a map of
|
|
299
|
+
* shortName → qualified type reference for use in callables.
|
|
300
|
+
*
|
|
301
|
+
* In flat mode, returns declarations like `export type ${prefix}${shortName} = ...`
|
|
302
|
+
* and refs like `${prefix}${shortName}`.
|
|
303
|
+
*/
|
|
304
|
+
async function formatSubNamespace(
|
|
305
|
+
routePascal: string,
|
|
306
|
+
nsName: string, // e.g. 'Req' or 'Response'
|
|
307
|
+
types: NamedType[],
|
|
308
|
+
ctx: EmitRouteContext,
|
|
309
|
+
taken: Set<string>,
|
|
310
|
+
): Promise<{ nsBlock: string | null; refs: Record<string, string> }> {
|
|
311
|
+
const refs: Record<string, string> = {}
|
|
312
|
+
const nsLines: string[] = []
|
|
313
|
+
const seenDeclarations = new Set<string>()
|
|
314
|
+
|
|
315
|
+
// Pre-reserve short names to prevent sub-type extraction collision
|
|
316
|
+
for (const t of types) {
|
|
317
|
+
if (t.schema != null) taken.add(t.shortName)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
for (const { shortName, schema } of types) {
|
|
321
|
+
if (schema == null) continue
|
|
322
|
+
|
|
323
|
+
if (ctx.namespaceTypes) {
|
|
324
|
+
const rawResult = await jsonSchemaToExtractedTypes(schema, ctx.ajsc)
|
|
325
|
+
if (rawResult == null) continue
|
|
326
|
+
|
|
327
|
+
const result = renameExtractedTypes(rawResult, taken)
|
|
328
|
+
|
|
329
|
+
for (const decl of result.declarations) {
|
|
330
|
+
if (!seenDeclarations.has(decl)) {
|
|
331
|
+
seenDeclarations.add(decl)
|
|
332
|
+
nsLines.push(indent(decl, ' '))
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
nsLines.push(` export type ${shortName} = ${result.body}`)
|
|
337
|
+
refs[shortName] = `${ctx.scopePascal}.${routePascal}.${nsName}.${shortName}`
|
|
338
|
+
} else {
|
|
339
|
+
const flatName = `${routePascal}${nsName}${shortName}`
|
|
340
|
+
const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
|
|
341
|
+
if (body == null) continue
|
|
342
|
+
refs[shortName] = flatName
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (ctx.namespaceTypes) {
|
|
347
|
+
if (nsLines.length === 0) return { nsBlock: null, refs }
|
|
348
|
+
const nsBlock = ` export namespace ${nsName} {\n${nsLines.join('\n')}\n }`
|
|
349
|
+
return { nsBlock, refs }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return { nsBlock: null, refs }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Builds the conditional return type string for an API or http-stream callable.
|
|
357
|
+
*
|
|
358
|
+
* - Both body + headers → `{ body: <Body>; headers: <Headers> }`
|
|
359
|
+
* - Only body → `<Body>`
|
|
360
|
+
* - Only headers → `{ headers: <Headers> }`
|
|
361
|
+
* - Neither → `void`
|
|
362
|
+
*/
|
|
363
|
+
function buildApiReturnType(
|
|
364
|
+
bodyRef: string | undefined,
|
|
365
|
+
headersRef: string | undefined,
|
|
366
|
+
): string {
|
|
367
|
+
if (bodyRef && headersRef) {
|
|
368
|
+
return `{ body: ${bodyRef}; headers: ${headersRef} }`
|
|
369
|
+
}
|
|
370
|
+
if (bodyRef) return bodyRef
|
|
371
|
+
if (headersRef) return `{ headers: ${headersRef} }`
|
|
372
|
+
return 'void'
|
|
373
|
+
}
|
|
374
|
+
|
|
294
375
|
async function emitApiRoute(route: APIHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
295
376
|
const pascal = toPascalCase(route.name)
|
|
296
|
-
const
|
|
377
|
+
const req = route.jsonSchema.req ?? {}
|
|
378
|
+
const res = route.jsonSchema.res ?? {}
|
|
297
379
|
|
|
298
|
-
//
|
|
299
|
-
const
|
|
380
|
+
// Request channels
|
|
381
|
+
const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
|
|
382
|
+
const reqTypes: NamedType[] = []
|
|
300
383
|
const presentChannels: string[] = []
|
|
301
384
|
|
|
302
|
-
for (const channel of
|
|
303
|
-
const
|
|
304
|
-
if (
|
|
305
|
-
|
|
385
|
+
for (const channel of reqChannelKeys) {
|
|
386
|
+
const schema = req[channel]
|
|
387
|
+
if (schema != null) {
|
|
388
|
+
reqTypes.push({ shortName: toPascalCase(channel), schema })
|
|
306
389
|
presentChannels.push(channel)
|
|
307
390
|
}
|
|
308
391
|
}
|
|
309
392
|
|
|
310
|
-
//
|
|
311
|
-
|
|
393
|
+
// Response slots
|
|
394
|
+
const resTypes: NamedType[] = [
|
|
395
|
+
{ shortName: 'Body', schema: res.body },
|
|
396
|
+
{ shortName: 'Headers', schema: res.headers },
|
|
397
|
+
]
|
|
312
398
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
new Set(['Params']),
|
|
320
|
-
)
|
|
399
|
+
const scopeStr = route.scope ?? 'default'
|
|
400
|
+
const errorUnion = buildErrorUnion(route.errors, ctx)
|
|
401
|
+
const hasErrors = errorUnion !== null
|
|
402
|
+
const errorsRef = ctx.namespaceTypes
|
|
403
|
+
? `${ctx.scopePascal}.${pascal}.Errors`
|
|
404
|
+
: `${pascal}Errors`
|
|
321
405
|
|
|
322
|
-
|
|
406
|
+
const declarations: string[] = []
|
|
323
407
|
let paramsTypeName = 'unknown'
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
408
|
+
let returnTypeName = 'void'
|
|
409
|
+
|
|
410
|
+
// Track reserved names across all sub-namespaces
|
|
411
|
+
const taken = new Set<string>(['Req', 'Response'])
|
|
412
|
+
|
|
413
|
+
if (ctx.namespaceTypes) {
|
|
414
|
+
// Namespace mode: emit nested Req {} and Response {} namespaces inside route namespace.
|
|
415
|
+
// Also emit merged type aliases `export type Req = { ... }` and `export type Response = ...`
|
|
416
|
+
// so they can be used as type arguments to bindCallable (TS requires a TYPE, not a namespace).
|
|
417
|
+
const nsLines: string[] = []
|
|
418
|
+
|
|
419
|
+
const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
|
|
420
|
+
pascal, 'Req', reqTypes, ctx, taken
|
|
421
|
+
)
|
|
422
|
+
if (reqBlock) {
|
|
423
|
+
nsLines.push(reqBlock)
|
|
424
|
+
// Merged type alias for Req so it can be used as a generic type arg
|
|
425
|
+
const reqFields = presentChannels
|
|
426
|
+
.map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
|
|
328
427
|
.join('; ')
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
428
|
+
nsLines.push(` export type Req = { ${reqFields} }`)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const { nsBlock: resBlock, refs: resRefs } = await formatSubNamespace(
|
|
432
|
+
pascal, 'Response', resTypes, ctx, taken
|
|
433
|
+
)
|
|
434
|
+
if (resBlock) {
|
|
435
|
+
nsLines.push(resBlock)
|
|
436
|
+
// No merged Response type alias needed: we reference Response.Body / Response.Headers
|
|
437
|
+
// directly in the return type string, which are namespace-qualified paths (valid).
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Emit Errors type last (injected by injectRouteErrors below)
|
|
441
|
+
// Build the route namespace block
|
|
442
|
+
if (nsLines.length > 0) {
|
|
443
|
+
declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n\n')}\n }`)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Params type: use the merged Req type alias
|
|
447
|
+
if (presentChannels.length > 0) {
|
|
448
|
+
paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Return type
|
|
452
|
+
const bodyRef = resRefs['Body']
|
|
453
|
+
const headersRef = resRefs['Headers']
|
|
454
|
+
returnTypeName = buildApiReturnType(bodyRef, headersRef)
|
|
455
|
+
} else {
|
|
456
|
+
// Flat mode: emit individual types prefixed with route + sub-namespace name
|
|
457
|
+
for (const { shortName, schema } of reqTypes) {
|
|
458
|
+
if (schema == null) continue
|
|
459
|
+
const flatName = `${pascal}Req${shortName}`
|
|
460
|
+
const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
|
|
461
|
+
if (body == null) continue
|
|
462
|
+
declarations.push(`export type ${flatName} = ${body}`)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Flat mode: compose structured Req type
|
|
466
|
+
if (presentChannels.length > 0) {
|
|
343
467
|
const structureFields = presentChannels
|
|
344
|
-
.map((ch) => `${ch}: ${
|
|
468
|
+
.map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
|
|
345
469
|
.join('; ')
|
|
346
|
-
declarations.push(`export type ${pascal}
|
|
347
|
-
paramsTypeName = `${pascal}
|
|
470
|
+
declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
|
|
471
|
+
paramsTypeName = `${pascal}Req`
|
|
348
472
|
}
|
|
349
|
-
}
|
|
350
473
|
|
|
351
|
-
|
|
352
|
-
|
|
474
|
+
// Flat mode: emit response types
|
|
475
|
+
let bodyRef: string | undefined
|
|
476
|
+
let headersRef: string | undefined
|
|
353
477
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
478
|
+
for (const { shortName, schema } of resTypes) {
|
|
479
|
+
if (schema == null) continue
|
|
480
|
+
const flatName = `${pascal}Response${shortName}`
|
|
481
|
+
const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
|
|
482
|
+
if (body == null) continue
|
|
483
|
+
declarations.push(`export type ${flatName} = ${body}`)
|
|
484
|
+
if (shortName === 'Body') bodyRef = flatName
|
|
485
|
+
if (shortName === 'Headers') headersRef = flatName
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
returnTypeName = buildApiReturnType(bodyRef, headersRef)
|
|
489
|
+
|
|
490
|
+
// Flat mode errors
|
|
491
|
+
if (errorUnion) {
|
|
492
|
+
declarations.push(`export type ${pascal}Errors = ${errorUnion}`)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const responseHeadersDeclared = res.headers != null
|
|
359
497
|
const helperCall = hasErrors
|
|
360
|
-
? `client.bindCallableTyped<${paramsTypeName}, ${
|
|
361
|
-
: `client.bindCallable<${paramsTypeName}, ${
|
|
498
|
+
? `client.bindCallableTyped<${paramsTypeName}, ${returnTypeName}, ${errorsRef}>`
|
|
499
|
+
: `client.bindCallable<${paramsTypeName}, ${returnTypeName}>`
|
|
362
500
|
|
|
363
|
-
|
|
364
|
-
const callable = [
|
|
365
|
-
` /** ${route.method.toUpperCase()} ${route.fullPath} */`,
|
|
366
|
-
` ${route.name}: ${helperCall}({`,
|
|
501
|
+
const descriptorLines = [
|
|
367
502
|
` name: '${route.name}',`,
|
|
368
503
|
` scope: '${scopeStr}',`,
|
|
369
504
|
` path: '${route.fullPath}',`,
|
|
370
505
|
` method: '${route.method}',`,
|
|
371
506
|
` kind: 'api',`,
|
|
507
|
+
...(responseHeadersDeclared ? [` responseHeadersDeclared: true,`] : []),
|
|
508
|
+
]
|
|
509
|
+
|
|
510
|
+
const callable = [
|
|
511
|
+
` /** ${route.method.toUpperCase()} ${route.fullPath} */`,
|
|
512
|
+
` ${route.name}: ${helperCall}({`,
|
|
513
|
+
...descriptorLines,
|
|
372
514
|
` }),`,
|
|
373
515
|
].join('\n')
|
|
374
516
|
|
|
375
|
-
const hasErrorsInjected =
|
|
517
|
+
const hasErrorsInjected = ctx.namespaceTypes
|
|
518
|
+
? injectRouteErrors(declarations, pascal, errorUnion, ctx.namespaceTypes)
|
|
519
|
+
: errorUnion !== null // flat mode already emitted errors above
|
|
376
520
|
|
|
377
521
|
return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
|
|
378
522
|
}
|
|
379
523
|
|
|
524
|
+
async function emitHttpStreamRoute(route: HttpStreamRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
525
|
+
const pascal = toPascalCase(route.name)
|
|
526
|
+
const req = route.jsonSchema.req ?? {}
|
|
527
|
+
const res = route.jsonSchema.res ?? {}
|
|
528
|
+
|
|
529
|
+
// Request channels
|
|
530
|
+
const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
|
|
531
|
+
const reqTypes: NamedType[] = []
|
|
532
|
+
const presentChannels: string[] = []
|
|
533
|
+
|
|
534
|
+
for (const channel of reqChannelKeys) {
|
|
535
|
+
const schema = req[channel]
|
|
536
|
+
if (schema != null) {
|
|
537
|
+
reqTypes.push({ shortName: toPascalCase(channel), schema })
|
|
538
|
+
presentChannels.push(channel)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Yield + ReturnType
|
|
543
|
+
const yieldSchema = route.jsonSchema.yield
|
|
544
|
+
const returnSchema = route.jsonSchema.returnType
|
|
545
|
+
const resHeadersSchema = res.headers
|
|
546
|
+
|
|
547
|
+
const scopeStr = route.scope ?? 'default'
|
|
548
|
+
const declarations: string[] = []
|
|
549
|
+
let paramsTypeName = 'unknown'
|
|
550
|
+
let yieldTypeName = 'unknown'
|
|
551
|
+
let returnTypeName = 'void'
|
|
552
|
+
|
|
553
|
+
const taken = new Set<string>(['Req', 'Response', 'Yield', 'ReturnType'])
|
|
554
|
+
|
|
555
|
+
if (ctx.namespaceTypes) {
|
|
556
|
+
const nsLines: string[] = []
|
|
557
|
+
|
|
558
|
+
// Req sub-namespace
|
|
559
|
+
const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
|
|
560
|
+
pascal, 'Req', reqTypes, ctx, taken
|
|
561
|
+
)
|
|
562
|
+
if (reqBlock) {
|
|
563
|
+
nsLines.push(reqBlock)
|
|
564
|
+
// Merged type alias so Req can be used as a generic type arg (same pattern as emitApiRoute)
|
|
565
|
+
const reqFields = presentChannels
|
|
566
|
+
.map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
|
|
567
|
+
.join('; ')
|
|
568
|
+
nsLines.push(` export type Req = { ${reqFields} }`)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Response sub-namespace (headers only for http-stream)
|
|
572
|
+
const resTypes: NamedType[] = [{ shortName: 'Headers', schema: resHeadersSchema }]
|
|
573
|
+
const { nsBlock: resBlock } = await formatSubNamespace(
|
|
574
|
+
pascal, 'Response', resTypes, ctx, taken
|
|
575
|
+
)
|
|
576
|
+
if (resBlock) nsLines.push(resBlock)
|
|
577
|
+
|
|
578
|
+
// Yield and ReturnType directly in the route namespace
|
|
579
|
+
const directTypes: NamedType[] = [
|
|
580
|
+
{ shortName: 'Yield', schema: yieldSchema },
|
|
581
|
+
{ shortName: 'ReturnType', schema: returnSchema },
|
|
582
|
+
]
|
|
583
|
+
const seenDeclarations = new Set<string>()
|
|
584
|
+
for (const { shortName, schema } of directTypes) {
|
|
585
|
+
if (schema == null) continue
|
|
586
|
+
const rawResult = await jsonSchemaToExtractedTypes(schema, ctx.ajsc)
|
|
587
|
+
if (rawResult == null) continue
|
|
588
|
+
const result = renameExtractedTypes(rawResult, taken)
|
|
589
|
+
for (const decl of result.declarations) {
|
|
590
|
+
if (!seenDeclarations.has(decl)) {
|
|
591
|
+
seenDeclarations.add(decl)
|
|
592
|
+
nsLines.push(indent(decl, ' '))
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
nsLines.push(` export type ${shortName} = ${result.body}`)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (nsLines.length > 0) {
|
|
599
|
+
declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n')}\n }`)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (presentChannels.length > 0) {
|
|
603
|
+
paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (yieldSchema != null) yieldTypeName = `${ctx.scopePascal}.${pascal}.Yield`
|
|
607
|
+
if (returnSchema != null) returnTypeName = `${ctx.scopePascal}.${pascal}.ReturnType`
|
|
608
|
+
} else {
|
|
609
|
+
// Flat mode
|
|
610
|
+
for (const { shortName, schema } of reqTypes) {
|
|
611
|
+
if (schema == null) continue
|
|
612
|
+
const flatName = `${pascal}Req${shortName}`
|
|
613
|
+
const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
|
|
614
|
+
if (body == null) continue
|
|
615
|
+
declarations.push(`export type ${flatName} = ${body}`)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (presentChannels.length > 0) {
|
|
619
|
+
const structureFields = presentChannels
|
|
620
|
+
.map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
|
|
621
|
+
.join('; ')
|
|
622
|
+
declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
|
|
623
|
+
paramsTypeName = `${pascal}Req`
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (resHeadersSchema != null) {
|
|
627
|
+
const body = await jsonSchemaToTypeBody(resHeadersSchema, ctx.ajsc)
|
|
628
|
+
if (body != null) declarations.push(`export type ${pascal}ResponseHeaders = ${body}`)
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (yieldSchema != null) {
|
|
632
|
+
const body = await jsonSchemaToTypeBody(yieldSchema, ctx.ajsc)
|
|
633
|
+
if (body != null) {
|
|
634
|
+
declarations.push(`export type ${pascal}Yield = ${body}`)
|
|
635
|
+
yieldTypeName = `${pascal}Yield`
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (returnSchema != null) {
|
|
640
|
+
const body = await jsonSchemaToTypeBody(returnSchema, ctx.ajsc)
|
|
641
|
+
if (body != null) {
|
|
642
|
+
declarations.push(`export type ${pascal}ReturnType = ${body}`)
|
|
643
|
+
returnTypeName = `${pascal}ReturnType`
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const callable = [
|
|
649
|
+
` /** ${route.method.toUpperCase()} ${route.fullPath} */`,
|
|
650
|
+
` ${route.name}(req: ${paramsTypeName}, options?: ProcedureCallOptions): TypedStream<${yieldTypeName}, ${returnTypeName}> {`,
|
|
651
|
+
` return client.stream<${yieldTypeName}, ${returnTypeName}>({`,
|
|
652
|
+
` name: '${route.name}',`,
|
|
653
|
+
` scope: '${scopeStr}',`,
|
|
654
|
+
` path: '${route.fullPath}',`,
|
|
655
|
+
` method: '${route.method}',`,
|
|
656
|
+
` kind: 'http-stream',`,
|
|
657
|
+
` streamMode: '${route.streamMode}',`,
|
|
658
|
+
` params: req,`,
|
|
659
|
+
` }, options)`,
|
|
660
|
+
` },`,
|
|
661
|
+
].join('\n')
|
|
662
|
+
|
|
663
|
+
const hasErrors = injectRouteErrors(
|
|
664
|
+
declarations,
|
|
665
|
+
pascal,
|
|
666
|
+
buildErrorUnion(route.errors, ctx),
|
|
667
|
+
ctx.namespaceTypes
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
return { typeDeclarations: declarations, callable, hasStream: true, hasErrors }
|
|
671
|
+
}
|
|
672
|
+
|
|
380
673
|
async function emitStreamRoute(route: StreamHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
381
674
|
const pascal = versionedPascal(route.name, route.version)
|
|
382
675
|
|
|
@@ -470,6 +763,8 @@ export async function emitScopeFile(
|
|
|
470
763
|
chunks = await emitApiRoute(route as APIHttpRouteDoc, ctx)
|
|
471
764
|
} else if (kind === 'stream') {
|
|
472
765
|
chunks = await emitStreamRoute(route as StreamHttpRouteDoc, ctx)
|
|
766
|
+
} else if (kind === 'http-stream') {
|
|
767
|
+
chunks = await emitHttpStreamRoute(route as HttpStreamRouteDoc, ctx)
|
|
473
768
|
} else {
|
|
474
769
|
throw new Error(`Unknown route kind "${kind}"`)
|
|
475
770
|
}
|
|
@@ -375,7 +375,7 @@ describe('runPipeline (kotlin target)', () => {
|
|
|
375
375
|
scope: 'users',
|
|
376
376
|
method: 'GET',
|
|
377
377
|
fullPath: '/users/:id',
|
|
378
|
-
|
|
378
|
+
jsonSchema: { req: { pathParams: { type: 'object' } }, res: { body: { type: 'object' } } },
|
|
379
379
|
errors: [],
|
|
380
380
|
},
|
|
381
381
|
],
|
|
@@ -438,7 +438,7 @@ describe('runPipeline (kotlin target)', () => {
|
|
|
438
438
|
routes: [
|
|
439
439
|
{
|
|
440
440
|
kind: 'api', name: 'GetUser', scope: 'users', method: 'GET', fullPath: '/users/:id',
|
|
441
|
-
|
|
441
|
+
jsonSchema: { req: { pathParams: { type: 'object' } }, res: { body: { type: 'object' } } },
|
|
442
442
|
errors: [],
|
|
443
443
|
},
|
|
444
444
|
],
|
|
@@ -466,9 +466,9 @@ describe('runPipeline (kotlin target)', () => {
|
|
|
466
466
|
const envelope = {
|
|
467
467
|
basePath: '/api', headers: [], version: '1' as const, errors: [],
|
|
468
468
|
routes: [
|
|
469
|
-
{ kind: 'stream', name: 'WatchA', scope: 's', method: 'GET', path: '/a',
|
|
470
|
-
{ kind: 'stream', name: 'WatchB', scope: 's', method: 'GET', path: '/b',
|
|
471
|
-
{ kind: 'api', name: 'GetThing', scope: 's', method: 'GET', fullPath: '/c',
|
|
469
|
+
{ kind: 'stream', name: 'WatchA', scope: 's', method: 'GET', path: '/a', jsonSchema: {}, errors: [] },
|
|
470
|
+
{ kind: 'stream', name: 'WatchB', scope: 's', method: 'GET', path: '/b', jsonSchema: {}, errors: [] },
|
|
471
|
+
{ kind: 'api', name: 'GetThing', scope: 's', method: 'GET', fullPath: '/c', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] },
|
|
472
472
|
],
|
|
473
473
|
} as any
|
|
474
474
|
await runPipeline({
|
|
@@ -493,7 +493,7 @@ describe('runPipeline (kotlin target)', () => {
|
|
|
493
493
|
try {
|
|
494
494
|
const envelope = {
|
|
495
495
|
basePath: '/api', headers: [], version: '1' as const, errors: [],
|
|
496
|
-
routes: [{ kind: 'api', name: 'X', scope: 's', method: 'GET', fullPath: '/x',
|
|
496
|
+
routes: [{ kind: 'api', name: 'X', scope: 's', method: 'GET', fullPath: '/x', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] }],
|
|
497
497
|
} as any
|
|
498
498
|
await runPipeline({
|
|
499
499
|
envelope, outDir: 'out', dryRun: true,
|
|
@@ -518,7 +518,7 @@ describe('runPipeline (kotlin target)', () => {
|
|
|
518
518
|
|
|
519
519
|
const envelope = {
|
|
520
520
|
basePath: '/api', headers: [], version: '1' as const, errors: [],
|
|
521
|
-
routes: [{ kind: 'api', name: 'GetUser', scope: 'users', method: 'GET', fullPath: '/u',
|
|
521
|
+
routes: [{ kind: 'api', name: 'GetUser', scope: 'users', method: 'GET', fullPath: '/u', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] }],
|
|
522
522
|
} as any
|
|
523
523
|
|
|
524
524
|
await runPipeline({
|
|
@@ -54,11 +54,11 @@ describe('resolveEnvelope', () => {
|
|
|
54
54
|
})
|
|
55
55
|
|
|
56
56
|
// Defensive (downstream bug repro): forgetting `builder.build()` is a common
|
|
57
|
-
// cause of an empty routes array because hono
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
57
|
+
// cause of an empty routes array because the hono builder only populates
|
|
58
|
+
// its `_docs` array inside `build()`. The current error message says
|
|
59
|
+
// "Register at least one procedure", which led the downstream dev to look
|
|
60
|
+
// in the wrong place. The message should mention `.build()` as a likely
|
|
61
|
+
// cause so the next person hits the right fix faster.
|
|
62
62
|
it('empty-routes error message mentions builder.build() as a likely cause', async () => {
|
|
63
63
|
const empty: DocEnvelope = { basePath: '', headers: [], errors: [], routes: [] }
|
|
64
64
|
await expect(resolveEnvelope({ envelope: empty })).rejects.toThrow(/\.build\(\)/)
|
|
@@ -55,7 +55,7 @@ export async function resolveEnvelope(input: ResolveInput): Promise<DocEnvelope>
|
|
|
55
55
|
throw new Error(
|
|
56
56
|
'[ts-procedures-codegen] DocEnvelope has an empty "routes" array. ' +
|
|
57
57
|
'Common causes: (1) you forgot to call `builder.build()` before passing ' +
|
|
58
|
-
'the builder to `DocRegistry.from(...)` — hono
|
|
58
|
+
'the builder to `DocRegistry.from(...)` — hono builders only populate ' +
|
|
59
59
|
'`docs` inside `build()`; (2) no procedures registered with the builder.'
|
|
60
60
|
)
|
|
61
61
|
}
|