ts-procedures 7.3.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -3
- package/agent_config/claude-code/agents/ts-procedures-architect.md +6 -8
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +30 -33
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +104 -53
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +205 -232
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +80 -153
- package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures-review/checklist.md +4 -5
- package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +4 -7
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono.md +223 -0
- package/agent_config/copilot/copilot-instructions.md +34 -48
- package/agent_config/cursor/cursorrules +34 -48
- package/build/client/call.js +4 -1
- package/build/client/call.js.map +1 -1
- package/build/client/call.test.js +23 -0
- package/build/client/call.test.js.map +1 -1
- package/build/client/fetch-adapter.js +3 -1
- package/build/client/fetch-adapter.js.map +1 -1
- package/build/client/fetch-adapter.test.js +11 -1
- package/build/client/fetch-adapter.test.js.map +1 -1
- package/build/client/index.test.js +7 -7
- package/build/client/index.test.js.map +1 -1
- package/build/client/request-builder.d.ts +1 -1
- package/build/client/request-builder.js +2 -2
- package/build/client/request-builder.js.map +1 -1
- package/build/client/stream.js +13 -2
- package/build/client/stream.js.map +1 -1
- package/build/client/stream.test.js +32 -7
- package/build/client/stream.test.js.map +1 -1
- package/build/client/typed-error-dispatch.test.js +8 -92
- package/build/client/typed-error-dispatch.test.js.map +1 -1
- package/build/client/types.d.ts +21 -3
- package/build/codegen/bin/cli.js +0 -0
- package/build/codegen/e2e.test.js +87 -23
- package/build/codegen/e2e.test.js.map +1 -1
- package/build/codegen/emit-errors.integration.test.js +1 -1
- package/build/codegen/emit-errors.integration.test.js.map +1 -1
- package/build/codegen/emit-scope.js +308 -47
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +363 -110
- package/build/codegen/emit-scope.test.js.map +1 -1
- package/build/codegen/pipeline.test.js +7 -7
- package/build/codegen/pipeline.test.js.map +1 -1
- package/build/codegen/resolve-envelope.js +1 -1
- package/build/codegen/resolve-envelope.js.map +1 -1
- package/build/codegen/resolve-envelope.test.js +5 -5
- package/build/codegen/resolve-envelope.test.js.map +1 -1
- package/build/codegen/targets/_shared/route-slots.d.ts +8 -3
- package/build/codegen/targets/_shared/route-slots.js +49 -8
- package/build/codegen/targets/_shared/route-slots.js.map +1 -1
- package/build/codegen/targets/_shared/route-slots.test.js +99 -26
- package/build/codegen/targets/_shared/route-slots.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js +88 -17
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js +9 -6
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/integration.test.js +6 -0
- package/build/codegen/targets/kotlin/integration.test.js.map +1 -1
- package/build/codegen/targets/swift/access-level.test.js +8 -11
- package/build/codegen/targets/swift/access-level.test.js.map +1 -1
- package/build/codegen/targets/swift/emit-route-swift.test.js +91 -20
- package/build/codegen/targets/swift/emit-route-swift.test.js.map +1 -1
- package/build/codegen/targets/swift/emit-scope-swift.test.js +12 -9
- package/build/codegen/targets/swift/emit-scope-swift.test.js.map +1 -1
- package/build/codegen/targets/swift/integration.test.js +6 -0
- package/build/codegen/targets/swift/integration.test.js.map +1 -1
- package/build/create-http-stream.d.ts +58 -0
- package/build/create-http-stream.js +122 -0
- package/build/create-http-stream.js.map +1 -0
- package/build/create-http-stream.test.js +88 -0
- package/build/create-http-stream.test.js.map +1 -0
- package/build/create-http.d.ts +49 -0
- package/build/create-http.js +108 -0
- package/build/create-http.js.map +1 -0
- package/build/create-http.test.js +137 -0
- package/build/create-http.test.js.map +1 -0
- package/build/create-stream.d.ts +35 -0
- package/build/create-stream.js +123 -0
- package/build/create-stream.js.map +1 -0
- package/build/create-stream.test.js +428 -0
- package/build/create-stream.test.js.map +1 -0
- package/build/create.d.ts +28 -0
- package/build/create.js +82 -0
- package/build/create.js.map +1 -0
- package/build/create.test.js +483 -0
- package/build/create.test.js.map +1 -0
- package/build/exports.d.ts +2 -0
- package/build/implementations/http/astro/index.test.js +20 -12
- package/build/implementations/http/astro/index.test.js.map +1 -1
- package/build/implementations/http/doc-registry.js +1 -1
- package/build/implementations/http/doc-registry.js.map +1 -1
- package/build/implementations/http/doc-registry.test.js +36 -5
- package/build/implementations/http/doc-registry.test.js.map +1 -1
- package/build/implementations/http/error-dispatch.d.ts +76 -0
- package/build/implementations/http/error-dispatch.js +77 -0
- package/build/implementations/http/error-dispatch.js.map +1 -0
- package/build/implementations/http/error-dispatch.test.js +254 -0
- package/build/implementations/http/error-dispatch.test.js.map +1 -0
- package/build/implementations/http/error-taxonomy.d.ts +5 -5
- package/build/implementations/http/hono/docs/http-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/http-doc.js +42 -0
- package/build/implementations/http/hono/docs/http-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/http-stream-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/http-stream-doc.js +40 -0
- package/build/implementations/http/hono/docs/http-stream-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/rpc-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/rpc-doc.js +24 -0
- package/build/implementations/http/hono/docs/rpc-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/stream-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/stream-doc.js +42 -0
- package/build/implementations/http/hono/docs/stream-doc.js.map +1 -0
- package/build/implementations/http/hono/handlers/http-stream.d.ts +10 -0
- package/build/implementations/http/hono/handlers/http-stream.js +123 -0
- package/build/implementations/http/hono/handlers/http-stream.js.map +1 -0
- package/build/implementations/http/hono/handlers/http-stream.test.js +128 -0
- package/build/implementations/http/hono/handlers/http-stream.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/http.d.ts +10 -0
- package/build/implementations/http/hono/handlers/http.js +115 -0
- package/build/implementations/http/hono/handlers/http.js.map +1 -0
- package/build/implementations/http/hono/handlers/http.test.js +118 -0
- package/build/implementations/http/hono/handlers/http.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/rpc.d.ts +11 -0
- package/build/implementations/http/hono/handlers/rpc.js +32 -0
- package/build/implementations/http/hono/handlers/rpc.js.map +1 -0
- package/build/implementations/http/hono/handlers/rpc.test.js +73 -0
- package/build/implementations/http/hono/handlers/rpc.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/stream.d.ts +23 -0
- package/build/implementations/http/hono/handlers/stream.js +147 -0
- package/build/implementations/http/hono/handlers/stream.js.map +1 -0
- package/build/implementations/http/hono/handlers/stream.test.d.ts +1 -0
- package/build/implementations/http/hono/handlers/stream.test.js +177 -0
- package/build/implementations/http/hono/handlers/stream.test.js.map +1 -0
- package/build/implementations/http/hono/index.d.ts +57 -0
- package/build/implementations/http/hono/index.js +149 -0
- package/build/implementations/http/hono/index.js.map +1 -0
- package/build/implementations/http/hono/index.test.d.ts +1 -0
- package/build/implementations/http/hono/index.test.js +274 -0
- package/build/implementations/http/hono/index.test.js.map +1 -0
- package/build/implementations/http/hono/path.d.ts +17 -0
- package/build/implementations/http/hono/path.js +39 -0
- package/build/implementations/http/hono/path.js.map +1 -0
- package/build/implementations/http/hono/path.test.d.ts +1 -0
- package/build/implementations/http/hono/path.test.js +83 -0
- package/build/implementations/http/hono/path.test.js.map +1 -0
- package/build/implementations/http/hono/types.d.ts +51 -0
- package/build/implementations/http/hono/types.js.map +1 -0
- package/build/implementations/http/on-request-error.test.js +6 -96
- package/build/implementations/http/on-request-error.test.js.map +1 -1
- package/build/implementations/http/route-errors.test.js +11 -59
- package/build/implementations/http/route-errors.test.js.map +1 -1
- package/build/implementations/types.d.ts +43 -9
- package/build/index.d.ts +124 -124
- package/build/index.js +10 -221
- package/build/index.js.map +1 -1
- package/build/index.test.js +20 -919
- package/build/index.test.js.map +1 -1
- package/build/migration.test.d.ts +1 -0
- package/build/migration.test.js +34 -0
- package/build/migration.test.js.map +1 -0
- package/build/schema/compute-schema.d.ts +11 -3
- package/build/schema/compute-schema.js +13 -7
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/parser.d.ts +11 -3
- package/build/schema/parser.js +49 -9
- package/build/schema/parser.js.map +1 -1
- package/build/stack-utils.js +8 -0
- package/build/stack-utils.js.map +1 -1
- package/build/types.d.ts +142 -0
- package/build/types.js.map +1 -0
- package/docs/astro-adapter.md +5 -5
- package/docs/core.md +15 -17
- package/docs/http-integrations.md +83 -170
- package/docs/streaming.md +3 -60
- package/docs/superpowers/plans/2026-05-07-astro-adapter.md +2 -7
- package/docs/superpowers/plans/2026-05-08-create-http.md +3355 -0
- package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +3365 -0
- package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +1 -3
- package/docs/superpowers/specs/2026-05-08-create-http-design.md +409 -0
- package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +411 -0
- package/package.json +4 -22
- package/src/client/call.test.ts +26 -0
- package/src/client/call.ts +4 -1
- package/src/client/fetch-adapter.test.ts +14 -1
- package/src/client/fetch-adapter.ts +3 -1
- package/src/client/index.test.ts +7 -7
- package/src/client/request-builder.ts +2 -2
- package/src/client/stream.test.ts +39 -7
- package/src/client/stream.ts +16 -2
- package/src/client/typed-error-dispatch.test.ts +7 -97
- package/src/client/types.ts +21 -3
- package/src/codegen/__fixtures__/users-envelope.json +119 -38
- package/src/codegen/e2e.test.ts +98 -24
- package/src/codegen/emit-errors.integration.test.ts +1 -1
- package/src/codegen/emit-scope.test.ts +395 -110
- package/src/codegen/emit-scope.ts +350 -55
- package/src/codegen/pipeline.test.ts +7 -7
- package/src/codegen/resolve-envelope.test.ts +5 -5
- package/src/codegen/resolve-envelope.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.test.ts +109 -26
- package/src/codegen/targets/_shared/route-slots.ts +48 -11
- package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +73 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +100 -17
- package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +9 -6
- package/src/codegen/targets/kotlin/integration.test.ts +19 -0
- package/src/codegen/targets/swift/__fixtures__/users-golden.swift +79 -0
- package/src/codegen/targets/swift/access-level.test.ts +8 -11
- package/src/codegen/targets/swift/emit-route-swift.test.ts +103 -20
- package/src/codegen/targets/swift/emit-scope-swift.test.ts +12 -9
- package/src/codegen/targets/swift/integration.test.ts +17 -0
- package/src/create-http-stream.test.ts +97 -0
- package/src/create-http-stream.ts +191 -0
- package/src/create-http.test.ts +163 -0
- package/src/create-http.ts +211 -0
- package/src/create-stream.test.ts +565 -0
- package/src/create-stream.ts +228 -0
- package/src/create.test.ts +658 -0
- package/src/create.ts +172 -0
- package/src/exports.ts +2 -0
- package/src/implementations/http/README.md +135 -95
- package/src/implementations/http/astro/README.md +4 -5
- package/src/implementations/http/astro/index.test.ts +25 -18
- package/src/implementations/http/doc-registry.test.ts +42 -5
- package/src/implementations/http/doc-registry.ts +1 -1
- package/src/implementations/http/error-dispatch.test.ts +283 -0
- package/src/implementations/http/error-dispatch.ts +176 -0
- package/src/implementations/http/error-taxonomy.ts +5 -5
- package/src/implementations/http/hono/docs/http-doc.ts +43 -0
- package/src/implementations/http/hono/docs/http-stream-doc.ts +44 -0
- package/src/implementations/http/hono/docs/rpc-doc.ts +34 -0
- package/src/implementations/http/hono/docs/stream-doc.ts +53 -0
- package/src/implementations/http/hono/handlers/http-stream.test.ts +150 -0
- package/src/implementations/http/hono/handlers/http-stream.ts +152 -0
- package/src/implementations/http/hono/handlers/http.test.ts +130 -0
- package/src/implementations/http/hono/handlers/http.ts +147 -0
- package/src/implementations/http/hono/handlers/rpc.test.ts +81 -0
- package/src/implementations/http/hono/handlers/rpc.ts +54 -0
- package/src/implementations/http/hono/handlers/stream.test.ts +198 -0
- package/src/implementations/http/hono/handlers/stream.ts +208 -0
- package/src/implementations/http/hono/index.test.ts +329 -0
- package/src/implementations/http/hono/index.ts +204 -0
- package/src/implementations/http/hono/path.test.ts +96 -0
- package/src/implementations/http/hono/path.ts +59 -0
- package/src/implementations/http/hono/types.ts +93 -0
- package/src/implementations/http/on-request-error.test.ts +10 -116
- package/src/implementations/http/route-errors.test.ts +11 -77
- package/src/implementations/types.ts +44 -9
- package/src/index.test.ts +22 -1249
- package/src/index.ts +49 -485
- package/src/migration.test.ts +48 -0
- package/src/schema/compute-schema.ts +26 -12
- package/src/schema/parser.ts +62 -12
- package/src/stack-utils.ts +8 -0
- package/src/types.ts +133 -0
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +0 -137
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +0 -173
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +0 -142
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +0 -147
- package/build/implementations/http/express-rpc/error-taxonomy.test.js +0 -83
- package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/express-rpc/index.d.ts +0 -125
- package/build/implementations/http/express-rpc/index.js +0 -216
- package/build/implementations/http/express-rpc/index.js.map +0 -1
- package/build/implementations/http/express-rpc/index.test.js +0 -684
- package/build/implementations/http/express-rpc/index.test.js.map +0 -1
- package/build/implementations/http/express-rpc/types.d.ts +0 -11
- package/build/implementations/http/express-rpc/types.js.map +0 -1
- package/build/implementations/http/hono-api/error-taxonomy.test.js +0 -137
- package/build/implementations/http/hono-api/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-api/index.d.ts +0 -151
- package/build/implementations/http/hono-api/index.js +0 -344
- package/build/implementations/http/hono-api/index.js.map +0 -1
- package/build/implementations/http/hono-api/index.test.js +0 -992
- package/build/implementations/http/hono-api/index.test.js.map +0 -1
- package/build/implementations/http/hono-api/types.d.ts +0 -13
- package/build/implementations/http/hono-api/types.js.map +0 -1
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js +0 -64
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-rpc/index.d.ts +0 -130
- package/build/implementations/http/hono-rpc/index.js +0 -209
- package/build/implementations/http/hono-rpc/index.js.map +0 -1
- package/build/implementations/http/hono-rpc/index.test.js +0 -828
- package/build/implementations/http/hono-rpc/index.test.js.map +0 -1
- package/build/implementations/http/hono-rpc/types.d.ts +0 -11
- package/build/implementations/http/hono-rpc/types.js +0 -2
- package/build/implementations/http/hono-rpc/types.js.map +0 -1
- package/build/implementations/http/hono-stream/error-taxonomy.test.js +0 -159
- package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-stream/index.d.ts +0 -171
- package/build/implementations/http/hono-stream/index.js +0 -415
- package/build/implementations/http/hono-stream/index.js.map +0 -1
- package/build/implementations/http/hono-stream/index.test.js +0 -1383
- package/build/implementations/http/hono-stream/index.test.js.map +0 -1
- package/build/implementations/http/hono-stream/types.d.ts +0 -15
- package/build/implementations/http/hono-stream/types.js +0 -2
- package/build/implementations/http/hono-stream/types.js.map +0 -1
- package/src/implementations/http/express-rpc/README.md +0 -280
- package/src/implementations/http/express-rpc/error-taxonomy.test.ts +0 -103
- package/src/implementations/http/express-rpc/index.test.ts +0 -957
- package/src/implementations/http/express-rpc/index.ts +0 -327
- package/src/implementations/http/express-rpc/types.ts +0 -16
- package/src/implementations/http/hono-api/README.md +0 -284
- package/src/implementations/http/hono-api/error-taxonomy.test.ts +0 -179
- package/src/implementations/http/hono-api/index.test.ts +0 -1341
- package/src/implementations/http/hono-api/index.ts +0 -519
- package/src/implementations/http/hono-api/types.ts +0 -16
- package/src/implementations/http/hono-rpc/README.md +0 -357
- package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +0 -82
- package/src/implementations/http/hono-rpc/index.test.ts +0 -1107
- package/src/implementations/http/hono-rpc/index.ts +0 -320
- package/src/implementations/http/hono-rpc/types.ts +0 -16
- package/src/implementations/http/hono-stream/README.md +0 -559
- package/src/implementations/http/hono-stream/error-taxonomy.test.ts +0 -178
- package/src/implementations/http/hono-stream/index.test.ts +0 -1804
- package/src/implementations/http/hono-stream/index.ts +0 -622
- package/src/implementations/http/hono-stream/types.ts +0 -20
- /package/build/{implementations/http/express-rpc/error-taxonomy.test.d.ts → create-http-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/express-rpc/index.test.d.ts → create-http.test.d.ts} +0 -0
- /package/build/{implementations/http/hono-api/error-taxonomy.test.d.ts → create-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/hono-api/index.test.d.ts → create.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-rpc/error-taxonomy.test.d.ts → error-dispatch.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-rpc/index.test.d.ts → hono/handlers/http-stream.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-stream/error-taxonomy.test.d.ts → hono/handlers/http.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-stream/index.test.d.ts → hono/handlers/rpc.test.d.ts} +0 -0
- /package/build/implementations/http/{express-rpc → hono}/types.js +0 -0
- /package/build/{implementations/http/hono-api/types.js → types.js} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,72 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from './
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import { Prettify, TJSONSchema } from './schema/types.js'
|
|
2
|
+
import { makeCreate } from './create.js'
|
|
3
|
+
import { makeCreateStream } from './create-stream.js'
|
|
4
|
+
import { makeCreateHttp } from './create-http.js'
|
|
5
|
+
import { makeCreateHttpStream } from './create-http-stream.js'
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
type TNoContextProvided,
|
|
9
|
+
type TLocalContext,
|
|
10
|
+
type TStreamContext,
|
|
11
|
+
type ProcedureKind,
|
|
12
|
+
type TProcedureRegistration,
|
|
13
|
+
type TStreamProcedureRegistration,
|
|
14
|
+
type THttpProcedureRegistration,
|
|
15
|
+
type THttpStreamProcedureRegistration,
|
|
16
|
+
type TBuilderConfig,
|
|
17
|
+
} from './types.js'
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
description?: string
|
|
26
|
-
schema?: {
|
|
27
|
-
params?: TJSONSchema
|
|
28
|
-
returnType?: TJSONSchema
|
|
29
|
-
input?: Record<string, TJSONSchema>
|
|
30
|
-
}
|
|
31
|
-
validation?: {
|
|
32
|
-
params?: (params: any) => { errors?: any[] }
|
|
33
|
-
input?: Record<string, (value: any) => { errors?: any[] }>
|
|
34
|
-
}
|
|
35
|
-
} & TExtendedConfig
|
|
36
|
-
|
|
37
|
-
handler: (ctx: TContext, params?: any) => Promise<any>
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
|
|
41
|
-
name: string
|
|
42
|
-
isStream: true
|
|
43
|
-
config: {
|
|
44
|
-
description?: string
|
|
45
|
-
schema?: {
|
|
46
|
-
params?: TJSONSchema
|
|
47
|
-
yieldType?: TJSONSchema
|
|
48
|
-
returnType?: TJSONSchema
|
|
49
|
-
input?: Record<string, TJSONSchema>
|
|
50
|
-
}
|
|
51
|
-
validation?: {
|
|
52
|
-
params?: (params: any) => { errors?: any[] }
|
|
53
|
-
yield?: (value: any) => { errors?: any[] }
|
|
54
|
-
input?: Record<string, (value: any) => { errors?: any[] }>
|
|
55
|
-
}
|
|
56
|
-
} & TExtendedConfig
|
|
57
|
-
|
|
58
|
-
handler: (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export type TBuilderConfig = {
|
|
62
|
-
/**
|
|
63
|
-
* The default is to validate schema inputs at runtime, to disable pass `true` for this property.
|
|
64
|
-
*
|
|
65
|
-
* @Note: this is not recommended for public unknown data, only use for internal procedure
|
|
66
|
-
* factories with type-checking build steps.
|
|
67
|
-
*/
|
|
68
|
-
noRuntimeValidation?: true
|
|
69
|
-
}
|
|
19
|
+
import {
|
|
20
|
+
TNoContextProvided,
|
|
21
|
+
TBuilderConfig,
|
|
22
|
+
TProcedureRegistration,
|
|
23
|
+
TStreamProcedureRegistration,
|
|
24
|
+
THttpProcedureRegistration,
|
|
25
|
+
THttpStreamProcedureRegistration,
|
|
26
|
+
} from './types.js'
|
|
70
27
|
|
|
71
28
|
export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unknown>(
|
|
72
29
|
/**
|
|
@@ -74,30 +31,23 @@ export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unkn
|
|
|
74
31
|
*/
|
|
75
32
|
builder?: {
|
|
76
33
|
config?: TBuilderConfig
|
|
34
|
+
/**
|
|
35
|
+
* Called once per registered procedure. Receives the full registration
|
|
36
|
+
* object for any of the four procedure kinds:
|
|
37
|
+
* - `TProcedureRegistration` (Create)
|
|
38
|
+
* - `TStreamProcedureRegistration` (CreateStream)
|
|
39
|
+
* - `THttpProcedureRegistration` (CreateHttp)
|
|
40
|
+
* - `THttpStreamProcedureRegistration` (CreateHttpStream)
|
|
41
|
+
*
|
|
42
|
+
* Use the `kind` discriminant to narrow to the specific shape.
|
|
43
|
+
*/
|
|
77
44
|
onCreate?: (
|
|
78
|
-
procedure: Prettify<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
config: Prettify<
|
|
85
|
-
{
|
|
86
|
-
description?: string
|
|
87
|
-
schema?: {
|
|
88
|
-
params?: TJSONSchema
|
|
89
|
-
yieldType?: TJSONSchema
|
|
90
|
-
returnType?: TJSONSchema
|
|
91
|
-
input?: Record<string, TJSONSchema>
|
|
92
|
-
}
|
|
93
|
-
validation?: {
|
|
94
|
-
params?: (params: any) => { errors?: any[] }
|
|
95
|
-
yield?: (value: any) => { errors?: any[] }
|
|
96
|
-
input?: Record<string, (value: any) => { errors?: any[] }>
|
|
97
|
-
}
|
|
98
|
-
} & TExtendedConfig
|
|
99
|
-
>
|
|
100
|
-
}>
|
|
45
|
+
procedure: Prettify<
|
|
46
|
+
| TProcedureRegistration<TContext, TExtendedConfig>
|
|
47
|
+
| TStreamProcedureRegistration<TContext, TExtendedConfig>
|
|
48
|
+
| THttpProcedureRegistration<TContext>
|
|
49
|
+
| THttpStreamProcedureRegistration<TContext>
|
|
50
|
+
>
|
|
101
51
|
) => void
|
|
102
52
|
}
|
|
103
53
|
) {
|
|
@@ -105,402 +55,14 @@ export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unkn
|
|
|
105
55
|
string,
|
|
106
56
|
| TProcedureRegistration<TContext, TExtendedConfig>
|
|
107
57
|
| TStreamProcedureRegistration<TContext, TExtendedConfig>
|
|
58
|
+
| THttpProcedureRegistration<TContext>
|
|
59
|
+
| THttpStreamProcedureRegistration<TContext>
|
|
108
60
|
> = new Map()
|
|
109
61
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
TInput extends Record<string, unknown> | undefined = undefined,
|
|
115
|
-
>(
|
|
116
|
-
name: TName,
|
|
117
|
-
config: {
|
|
118
|
-
description?: string
|
|
119
|
-
schema?: {
|
|
120
|
-
params?: TParams
|
|
121
|
-
returnType?: TReturnType
|
|
122
|
-
input?: TInput
|
|
123
|
-
}
|
|
124
|
-
} & TExtendedConfig,
|
|
125
|
-
handler: (
|
|
126
|
-
ctx: Prettify<TContext & TLocalContext>,
|
|
127
|
-
params: TInput extends Record<string, unknown>
|
|
128
|
-
? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
|
|
129
|
-
: TSchemaLib<TParams>
|
|
130
|
-
) => Promise<TSchemaLib<TReturnType>>
|
|
131
|
-
) {
|
|
132
|
-
// Capture definition location as first action
|
|
133
|
-
const definitionInfo = captureDefinitionInfo()
|
|
134
|
-
|
|
135
|
-
// BEFORE computeSchema - fail fast on duplicate
|
|
136
|
-
if (procedures.has(name)) {
|
|
137
|
-
throw new Error(`Procedure with name ${name} is already registered`)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
|
|
141
|
-
|
|
142
|
-
// Create error factory once at registration time (outside handler)
|
|
143
|
-
const errorFactory = (message: string, meta?: object) => {
|
|
144
|
-
return new ProcedureError(name, message, meta, definitionInfo)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const registeredProcedure = {
|
|
148
|
-
name,
|
|
149
|
-
config: {
|
|
150
|
-
// ctx: config.hook, ??? why was this here
|
|
151
|
-
...config,
|
|
152
|
-
description: config.description,
|
|
153
|
-
schema: jsonSchema,
|
|
154
|
-
validation: {
|
|
155
|
-
params: validations.params,
|
|
156
|
-
...(validations.input && { input: validations.input }),
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
handler: async (ctx: Prettify<TContext>, params: TSchemaLib<TParams>) => {
|
|
161
|
-
try {
|
|
162
|
-
// Skip validation if caller has already validated (e.g., HonoStreamAppBuilder) or if builder config dictates noRuntimeValidation
|
|
163
|
-
const skipValidation =
|
|
164
|
-
(ctx as { isPrevalidated?: boolean }).isPrevalidated ||
|
|
165
|
-
builder?.config?.noRuntimeValidation
|
|
166
|
-
|
|
167
|
-
if (validations?.params && !skipValidation) {
|
|
168
|
-
const { errors } = validations.params(params)
|
|
169
|
-
|
|
170
|
-
if (errors) {
|
|
171
|
-
throw new ProcedureValidationError(
|
|
172
|
-
name,
|
|
173
|
-
`Validation error for ${name}`,
|
|
174
|
-
errors,
|
|
175
|
-
definitionInfo
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Validate each input channel independently for better error messages.
|
|
181
|
-
// Double validation (per-channel + merged) is intentional for developer experience;
|
|
182
|
-
// the cost is negligible — revisit if validation becomes a performance bottleneck.
|
|
183
|
-
if (validations?.input && !skipValidation) {
|
|
184
|
-
for (const [channel, validator] of Object.entries(validations.input)) {
|
|
185
|
-
const channelValue = (params as Record<string, unknown>)?.[channel]
|
|
186
|
-
const { errors } = validator(channelValue)
|
|
187
|
-
|
|
188
|
-
if (errors) {
|
|
189
|
-
throw new ProcedureValidationError(
|
|
190
|
-
name,
|
|
191
|
-
`Validation error for ${name} in input.${channel}`,
|
|
192
|
-
errors,
|
|
193
|
-
definitionInfo
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const localCtx: TLocalContext = {
|
|
200
|
-
error: errorFactory, // Reuse pre-created factory
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// params is correctly typed at the public API boundary via conditional type;
|
|
204
|
-
// cast here because TS cannot narrow conditional generics inside implementations
|
|
205
|
-
return await handler(
|
|
206
|
-
{
|
|
207
|
-
...ctx,
|
|
208
|
-
...localCtx,
|
|
209
|
-
} as Prettify<TContext & TLocalContext>,
|
|
210
|
-
params as any
|
|
211
|
-
)
|
|
212
|
-
} catch (error: any) {
|
|
213
|
-
if (error instanceof ProcedureError) {
|
|
214
|
-
throw error
|
|
215
|
-
} else {
|
|
216
|
-
const err = new ProcedureError(
|
|
217
|
-
name,
|
|
218
|
-
`Error in handler for ${name} - ${error?.message}`,
|
|
219
|
-
undefined,
|
|
220
|
-
definitionInfo
|
|
221
|
-
)
|
|
222
|
-
err.cause = error // Preserve original error
|
|
223
|
-
// Preserve original stack but append definition info
|
|
224
|
-
if (error.stack && definitionInfo.definedAt) {
|
|
225
|
-
const { file, line, column } = definitionInfo.definedAt
|
|
226
|
-
err.stack =
|
|
227
|
-
error.stack +
|
|
228
|
-
`\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
229
|
-
} else if (error.stack) {
|
|
230
|
-
err.stack = error.stack
|
|
231
|
-
}
|
|
232
|
-
throw err
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
procedures.set(name, registeredProcedure)
|
|
239
|
-
|
|
240
|
-
if (builder?.onCreate) {
|
|
241
|
-
builder.onCreate(registeredProcedure)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const info = {
|
|
245
|
-
name,
|
|
246
|
-
...registeredProcedure.config,
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// return so can be called directly (ie: int/unit tests)
|
|
250
|
-
return {
|
|
251
|
-
[name]: registeredProcedure.handler,
|
|
252
|
-
procedure: registeredProcedure.handler,
|
|
253
|
-
info,
|
|
254
|
-
} as {
|
|
255
|
-
[K in TName]: (
|
|
256
|
-
ctx: Prettify<TContext>,
|
|
257
|
-
params: TInput extends Record<string, unknown>
|
|
258
|
-
? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
|
|
259
|
-
: TSchemaLib<TParams>
|
|
260
|
-
) => Promise<TSchemaLib<TReturnType>>
|
|
261
|
-
} & {
|
|
262
|
-
procedure: (
|
|
263
|
-
ctx: Prettify<TContext>,
|
|
264
|
-
params: TInput extends Record<string, unknown>
|
|
265
|
-
? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
|
|
266
|
-
: TSchemaLib<TParams>
|
|
267
|
-
) => Promise<TSchemaLib<TReturnType>>
|
|
268
|
-
info: {
|
|
269
|
-
name: TName
|
|
270
|
-
description?: string
|
|
271
|
-
schema: {
|
|
272
|
-
params?: TParams
|
|
273
|
-
returnType?: TReturnType
|
|
274
|
-
input?: TInput
|
|
275
|
-
}
|
|
276
|
-
validation?: {
|
|
277
|
-
params?: (params: any) => { errors?: any[] }
|
|
278
|
-
input?: Record<string, (value: any) => { errors?: any[] }>
|
|
279
|
-
}
|
|
280
|
-
} & TExtendedConfig
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function CreateStream<
|
|
285
|
-
TName extends string,
|
|
286
|
-
TParams,
|
|
287
|
-
TYieldType,
|
|
288
|
-
TReturnType = void,
|
|
289
|
-
TInput extends Record<string, unknown> | undefined = undefined,
|
|
290
|
-
>(
|
|
291
|
-
name: TName,
|
|
292
|
-
config: {
|
|
293
|
-
description?: string
|
|
294
|
-
schema?: {
|
|
295
|
-
params?: TParams
|
|
296
|
-
yieldType?: TYieldType
|
|
297
|
-
returnType?: TReturnType
|
|
298
|
-
input?: TInput
|
|
299
|
-
}
|
|
300
|
-
validateYields?: boolean
|
|
301
|
-
} & TExtendedConfig,
|
|
302
|
-
handler: (
|
|
303
|
-
ctx: Prettify<TContext & TStreamContext>,
|
|
304
|
-
params: TInput extends Record<string, unknown>
|
|
305
|
-
? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
|
|
306
|
-
: TSchemaLib<TParams>
|
|
307
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
308
|
-
) {
|
|
309
|
-
// Capture definition location as first action
|
|
310
|
-
const definitionInfo = captureDefinitionInfo()
|
|
311
|
-
|
|
312
|
-
// BEFORE computeSchema - fail fast on duplicate
|
|
313
|
-
if (procedures.has(name)) {
|
|
314
|
-
throw new Error(`Procedure with name ${name} is already registered`)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
|
|
318
|
-
|
|
319
|
-
// Create error factory once at registration time (outside handler)
|
|
320
|
-
const errorFactory = (message: string, meta?: object) => {
|
|
321
|
-
return new ProcedureError(name, message, meta, definitionInfo)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const validateYields = config.validateYields ?? false
|
|
325
|
-
|
|
326
|
-
const registeredProcedure: TStreamProcedureRegistration<TContext, TExtendedConfig> = {
|
|
327
|
-
name,
|
|
328
|
-
isStream: true,
|
|
329
|
-
config: {
|
|
330
|
-
...config,
|
|
331
|
-
description: config.description,
|
|
332
|
-
schema: jsonSchema,
|
|
333
|
-
validation: {
|
|
334
|
-
params: validations.params,
|
|
335
|
-
yield: validations.yield,
|
|
336
|
-
...(validations.input && { input: validations.input }),
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
handler: async function* wrappedHandler(
|
|
341
|
-
ctx: Prettify<TContext>,
|
|
342
|
-
params: TSchemaLib<TParams>
|
|
343
|
-
) {
|
|
344
|
-
// Create abort controller for this stream
|
|
345
|
-
const abortController = new AbortController()
|
|
346
|
-
|
|
347
|
-
// Skip validation if caller has already validated (e.g., HonoStreamAppBuilder) or if builder config dictates noRuntimeValidation
|
|
348
|
-
const skipValidation =
|
|
349
|
-
(ctx as { isPrevalidated?: boolean }).isPrevalidated ||
|
|
350
|
-
builder?.config?.noRuntimeValidation
|
|
351
|
-
|
|
352
|
-
if (validations?.params && !skipValidation) {
|
|
353
|
-
const { errors } = validations.params(params)
|
|
354
|
-
|
|
355
|
-
if (errors) {
|
|
356
|
-
throw new ProcedureValidationError(
|
|
357
|
-
name,
|
|
358
|
-
`Validation error for ${name}`,
|
|
359
|
-
errors,
|
|
360
|
-
definitionInfo
|
|
361
|
-
)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Validate each input channel independently (see Create for rationale)
|
|
366
|
-
if (validations?.input && !skipValidation) {
|
|
367
|
-
for (const [channel, validator] of Object.entries(validations.input)) {
|
|
368
|
-
const channelValue = (params as Record<string, unknown>)?.[channel]
|
|
369
|
-
const { errors } = validator(channelValue)
|
|
370
|
-
|
|
371
|
-
if (errors) {
|
|
372
|
-
throw new ProcedureValidationError(
|
|
373
|
-
name,
|
|
374
|
-
`Validation error for ${name} in input.${channel}`,
|
|
375
|
-
errors,
|
|
376
|
-
definitionInfo
|
|
377
|
-
)
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Combine with external signal (e.g., from HTTP request) if provided
|
|
383
|
-
const incomingSignal = (ctx as { signal?: AbortSignal }).signal
|
|
384
|
-
const signal = incomingSignal
|
|
385
|
-
? AbortSignal.any([incomingSignal, abortController.signal])
|
|
386
|
-
: abortController.signal
|
|
387
|
-
|
|
388
|
-
const streamCtx: TStreamContext = {
|
|
389
|
-
error: errorFactory,
|
|
390
|
-
signal,
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// params is correctly typed at the public API boundary via conditional type;
|
|
394
|
-
// cast here because TS cannot narrow conditional generics inside implementations
|
|
395
|
-
const userGenerator = handler(
|
|
396
|
-
{
|
|
397
|
-
...ctx,
|
|
398
|
-
...streamCtx,
|
|
399
|
-
} as Prettify<TContext & TStreamContext>,
|
|
400
|
-
params as any
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
const userIterator = userGenerator[Symbol.asyncIterator]()
|
|
404
|
-
try {
|
|
405
|
-
let userIterResult = await userIterator.next()
|
|
406
|
-
|
|
407
|
-
while (!userIterResult.done) {
|
|
408
|
-
const value = userIterResult.value
|
|
409
|
-
|
|
410
|
-
// Only validate if explicitly enabled via validateYields: true
|
|
411
|
-
if (validateYields && validations.yield) {
|
|
412
|
-
const { errors } = validations.yield(value)
|
|
413
|
-
if (errors) {
|
|
414
|
-
throw new ProcedureYieldValidationError(
|
|
415
|
-
name,
|
|
416
|
-
`Yield validation error for ${name}`,
|
|
417
|
-
errors,
|
|
418
|
-
definitionInfo
|
|
419
|
-
)
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
yield value
|
|
424
|
-
userIterResult = await userIterator.next()
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Propagate the generator's return value so consumers (e.g. HonoStreamAppBuilder)
|
|
428
|
-
// can send it as a special 'return' SSE event
|
|
429
|
-
return userIterResult.value
|
|
430
|
-
} catch (error: any) {
|
|
431
|
-
// Preserve the original error class so HTTP builders' taxonomies and
|
|
432
|
-
// `onMidStreamError` callbacks see the actual thrown type — boxing
|
|
433
|
-
// user-defined errors inside ProcedureError defeats route-declared
|
|
434
|
-
// typed-error dispatch on the client. Augment the stack trace in
|
|
435
|
-
// place with the procedure's definition site when available.
|
|
436
|
-
if (definitionInfo.definedAt && error && typeof error.stack === 'string') {
|
|
437
|
-
const { file, line, column } = definitionInfo.definedAt
|
|
438
|
-
error.stack = `${error.stack}\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
439
|
-
}
|
|
440
|
-
throw error
|
|
441
|
-
} finally {
|
|
442
|
-
// Propagate `.return()` to the user generator so its `finally`
|
|
443
|
-
// blocks (and any `signal`-driven cleanup) run when the consumer
|
|
444
|
-
// closes the stream early. No-op when iteration already completed.
|
|
445
|
-
try {
|
|
446
|
-
await userIterator.return?.(undefined)
|
|
447
|
-
} catch {
|
|
448
|
-
// Swallow — cleanup must not mask the primary error path
|
|
449
|
-
}
|
|
450
|
-
abortController.abort('stream-completed')
|
|
451
|
-
}
|
|
452
|
-
} as (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>,
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
procedures.set(name, registeredProcedure)
|
|
456
|
-
|
|
457
|
-
if (builder?.onCreate) {
|
|
458
|
-
builder.onCreate(registeredProcedure)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const info = {
|
|
462
|
-
name,
|
|
463
|
-
isStream: true as const,
|
|
464
|
-
...registeredProcedure.config,
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// return so can be called directly (ie: int/unit tests)
|
|
468
|
-
return {
|
|
469
|
-
[name]: registeredProcedure.handler,
|
|
470
|
-
procedure: registeredProcedure.handler,
|
|
471
|
-
info,
|
|
472
|
-
} as {
|
|
473
|
-
[K in TName]: (
|
|
474
|
-
ctx: Prettify<TContext>,
|
|
475
|
-
params: TInput extends Record<string, unknown>
|
|
476
|
-
? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
|
|
477
|
-
: TSchemaLib<TParams>
|
|
478
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
479
|
-
} & {
|
|
480
|
-
procedure: (
|
|
481
|
-
ctx: Prettify<TContext>,
|
|
482
|
-
params: TInput extends Record<string, unknown>
|
|
483
|
-
? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
|
|
484
|
-
: TSchemaLib<TParams>
|
|
485
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
486
|
-
info: {
|
|
487
|
-
name: TName
|
|
488
|
-
isStream: true
|
|
489
|
-
description?: string
|
|
490
|
-
schema: {
|
|
491
|
-
params?: TParams
|
|
492
|
-
yieldType?: TYieldType
|
|
493
|
-
returnType?: TReturnType
|
|
494
|
-
input?: TInput
|
|
495
|
-
}
|
|
496
|
-
validation?: {
|
|
497
|
-
params?: (params: any) => { errors?: any[] }
|
|
498
|
-
yield?: (value: any) => { errors?: any[] }
|
|
499
|
-
input?: Record<string, (value: any) => { errors?: any[] }>
|
|
500
|
-
}
|
|
501
|
-
} & TExtendedConfig
|
|
502
|
-
}
|
|
503
|
-
}
|
|
62
|
+
const Create = makeCreate<TContext, TExtendedConfig>(procedures, builder)
|
|
63
|
+
const CreateStream = makeCreateStream<TContext, TExtendedConfig>(procedures, builder)
|
|
64
|
+
const CreateHttp = makeCreateHttp<TContext>(procedures, builder)
|
|
65
|
+
const CreateHttpStream = makeCreateHttpStream<TContext>(procedures, builder)
|
|
504
66
|
|
|
505
67
|
return {
|
|
506
68
|
/**
|
|
@@ -533,5 +95,7 @@ export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unkn
|
|
|
533
95
|
|
|
534
96
|
Create,
|
|
535
97
|
CreateStream,
|
|
98
|
+
CreateHttp,
|
|
99
|
+
CreateHttpStream,
|
|
536
100
|
}
|
|
537
101
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { Procedures } from './index.js'
|
|
3
|
+
import { ProcedureRegistrationError } from './errors.js'
|
|
4
|
+
import { Type } from 'typebox'
|
|
5
|
+
|
|
6
|
+
describe('v7 → v8 migration errors', () => {
|
|
7
|
+
it('rejects schema.input on Create with v8 message', () => {
|
|
8
|
+
const procs = Procedures()
|
|
9
|
+
expect(() =>
|
|
10
|
+
procs.Create('Legacy', { schema: { input: { body: Type.Object({}) } } } as any, async () => undefined)
|
|
11
|
+
).toThrow(ProcedureRegistrationError)
|
|
12
|
+
expect(() =>
|
|
13
|
+
procs.Create('Legacy2', { schema: { input: { body: Type.Object({}) } } } as any, async () => undefined)
|
|
14
|
+
).toThrow(/schema.input was removed in v8/)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('rejects schema.input on CreateStream with v8 message', () => {
|
|
18
|
+
const procs = Procedures()
|
|
19
|
+
expect(() =>
|
|
20
|
+
procs.CreateStream('Legacy', {
|
|
21
|
+
schema: { input: { body: Type.Object({}) }, yieldType: Type.Number() } as any,
|
|
22
|
+
}, async function* () {})
|
|
23
|
+
).toThrow(/schema.input was removed in v8/)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('HTTP fields rejected on RPC creators', () => {
|
|
28
|
+
const HTTP_FIELDS = ['path', 'method', 'req', 'res', 'successStatus'] as const
|
|
29
|
+
|
|
30
|
+
for (const field of HTTP_FIELDS) {
|
|
31
|
+
it(`Create rejects ${field}`, () => {
|
|
32
|
+
const procs = Procedures()
|
|
33
|
+
expect(() =>
|
|
34
|
+
procs.Create('Foo', { [field]: 'x', schema: { params: Type.Object({}) } } as any, async () => undefined)
|
|
35
|
+
).toThrow(/HTTP fields require CreateHttp/)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it(`CreateStream rejects ${field}`, () => {
|
|
39
|
+
const procs = Procedures()
|
|
40
|
+
expect(() =>
|
|
41
|
+
procs.CreateStream('Bar', {
|
|
42
|
+
[field]: 'x',
|
|
43
|
+
schema: { params: Type.Object({}), yieldType: Type.Number() },
|
|
44
|
+
} as any, async function* () {})
|
|
45
|
+
).toThrow(/HTTP fields require CreateHttp/)
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
})
|
|
@@ -17,7 +17,8 @@ export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTy
|
|
|
17
17
|
params?: TParamsSchemaType
|
|
18
18
|
returnType?: TReturnTypeSchemaType
|
|
19
19
|
yieldType?: TYieldTypeSchemaType
|
|
20
|
-
|
|
20
|
+
req?: Record<string, unknown>
|
|
21
|
+
res?: { body?: unknown; headers?: unknown }
|
|
21
22
|
},
|
|
22
23
|
// Used for error stack trace details
|
|
23
24
|
definitionInfo?: DefinitionInfo
|
|
@@ -26,44 +27,56 @@ export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTy
|
|
|
26
27
|
params?: TJSONSchema
|
|
27
28
|
returnType?: TJSONSchema
|
|
28
29
|
yieldType?: TJSONSchema
|
|
29
|
-
|
|
30
|
+
req?: Record<string, TJSONSchema>
|
|
31
|
+
res?: { body?: TJSONSchema; headers?: TJSONSchema }
|
|
30
32
|
}
|
|
31
33
|
validations: {
|
|
32
34
|
params?: (params?: any) => { errors?: TSchemaValidationError[] }
|
|
33
35
|
yield?: (value?: any) => { errors?: TSchemaValidationError[] }
|
|
34
|
-
|
|
36
|
+
req?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
|
|
35
37
|
}
|
|
36
38
|
} {
|
|
37
39
|
const jsonSchema: {
|
|
38
40
|
params?: TJSONSchema
|
|
39
41
|
returnType?: TJSONSchema
|
|
40
42
|
yieldType?: TJSONSchema
|
|
41
|
-
|
|
43
|
+
req?: Record<string, TJSONSchema>
|
|
44
|
+
res?: { body?: TJSONSchema; headers?: TJSONSchema }
|
|
42
45
|
} = {
|
|
43
46
|
params: undefined,
|
|
44
47
|
returnType: undefined,
|
|
45
48
|
yieldType: undefined,
|
|
46
|
-
|
|
49
|
+
req: undefined,
|
|
50
|
+
res: undefined,
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
const validations: {
|
|
50
54
|
params?: (params?: any) => { errors?: TSchemaValidationError[] }
|
|
51
55
|
yield?: (value?: any) => { errors?: TSchemaValidationError[] }
|
|
52
|
-
|
|
56
|
+
req?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
|
|
53
57
|
} = {}
|
|
54
58
|
|
|
55
|
-
//
|
|
56
|
-
if (schema
|
|
59
|
+
// Migration guard: schema.input was removed in v8
|
|
60
|
+
if (schema && 'input' in schema && (schema as any).input !== undefined) {
|
|
57
61
|
throw new ProcedureRegistrationError(
|
|
58
62
|
name,
|
|
59
|
-
`schema.
|
|
63
|
+
`schema.input was removed in v8. Use CreateHttp / CreateHttpStream for per-channel HTTP validation. Procedure: "${name}".`,
|
|
64
|
+
definitionInfo,
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Mutual exclusivity: params and req cannot both be defined
|
|
69
|
+
if (schema?.params && schema?.req) {
|
|
70
|
+
throw new ProcedureRegistrationError(
|
|
71
|
+
name,
|
|
72
|
+
`schema.params and schema.req are mutually exclusive for procedure "${name}". Use schema.params for RPC procedures or schema.req for HTTP procedures.`,
|
|
60
73
|
definitionInfo
|
|
61
74
|
)
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
if (schema) {
|
|
65
78
|
const {
|
|
66
|
-
jsonSchema: { params, returnType, yieldType,
|
|
79
|
+
jsonSchema: { params, returnType, yieldType, req, res },
|
|
67
80
|
validation,
|
|
68
81
|
} = schemaParser(schema, (errors) => {
|
|
69
82
|
throw new ProcedureRegistrationError(
|
|
@@ -78,10 +91,11 @@ export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTy
|
|
|
78
91
|
jsonSchema.params = params
|
|
79
92
|
jsonSchema.returnType = returnType
|
|
80
93
|
jsonSchema.yieldType = yieldType
|
|
81
|
-
jsonSchema.
|
|
94
|
+
jsonSchema.req = req
|
|
95
|
+
jsonSchema.res = res
|
|
82
96
|
validations.params = validation.params
|
|
83
97
|
validations.yield = validation.yield
|
|
84
|
-
validations.
|
|
98
|
+
validations.req = validation.req
|
|
85
99
|
}
|
|
86
100
|
|
|
87
101
|
return { jsonSchema, validations }
|