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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { HttpMethod } from '../types.js'
|
|
2
|
+
import { extractQuery } from './query.js'
|
|
3
|
+
import type { QueryParser } from './query.js'
|
|
4
|
+
|
|
5
|
+
/** HTTP methods whose requests may carry a body. */
|
|
6
|
+
export const BODY_METHODS: HttpMethod[] = ['post', 'put', 'patch']
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adapter-provided view of an incoming request — the minimal surface needed
|
|
10
|
+
* to extract `schema.req` channels. Each server adapter implements this once
|
|
11
|
+
* (e.g. the Hono adapter wraps `Context`).
|
|
12
|
+
*/
|
|
13
|
+
export type RequestSource = {
|
|
14
|
+
/** Decoded path params (`/users/:id` → `{ id }`). */
|
|
15
|
+
pathParams(): Record<string, string>
|
|
16
|
+
/** The full request URL (query extraction slices it). */
|
|
17
|
+
url(): string
|
|
18
|
+
/** The parsed JSON body. Only called for body-carrying methods. */
|
|
19
|
+
json(): Promise<unknown>
|
|
20
|
+
/** All request headers, lowercased keys. */
|
|
21
|
+
headers(): Record<string, string>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Extracts the declared `schema.req` channels from a request. Only channels
|
|
26
|
+
* present in the schema are read — an undeclared body is never parsed, an
|
|
27
|
+
* undeclared header never observed. Body extraction is skipped for non-body
|
|
28
|
+
* methods. JSON parse failures yield `{}` (validation then reports the
|
|
29
|
+
* missing fields, which beats an opaque parse error).
|
|
30
|
+
*/
|
|
31
|
+
export async function extractReqChannels(
|
|
32
|
+
source: RequestSource,
|
|
33
|
+
method: HttpMethod,
|
|
34
|
+
reqSchema: Record<string, unknown>,
|
|
35
|
+
parser: QueryParser,
|
|
36
|
+
): Promise<Record<string, unknown>> {
|
|
37
|
+
const params: Record<string, unknown> = {}
|
|
38
|
+
for (const channel of Object.keys(reqSchema)) {
|
|
39
|
+
switch (channel) {
|
|
40
|
+
case 'pathParams':
|
|
41
|
+
params.pathParams = source.pathParams()
|
|
42
|
+
break
|
|
43
|
+
case 'query':
|
|
44
|
+
params.query = extractQuery(source.url(), parser)
|
|
45
|
+
break
|
|
46
|
+
case 'body':
|
|
47
|
+
if (BODY_METHODS.includes(method)) {
|
|
48
|
+
params.body = await source.json().catch(() => ({}))
|
|
49
|
+
}
|
|
50
|
+
break
|
|
51
|
+
case 'headers':
|
|
52
|
+
params.headers = source.headers()
|
|
53
|
+
break
|
|
54
|
+
default:
|
|
55
|
+
params[channel] = undefined
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return params
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Success-status defaults by method: POST→201, DELETE→204, others→200. */
|
|
62
|
+
export function defaultSuccessStatus(method: HttpMethod): number {
|
|
63
|
+
switch (method) {
|
|
64
|
+
case 'post': return 201
|
|
65
|
+
case 'delete': return 204
|
|
66
|
+
default: return 200
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { extractQuery, parseQueryNative } from './query.js'
|
|
3
|
+
|
|
4
|
+
describe('parseQueryNative', () => {
|
|
5
|
+
test('single-value keys remain scalars', () => {
|
|
6
|
+
expect(parseQueryNative('a=1&b=two')).toEqual({ a: '1', b: 'two' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test('multi-value keys become arrays', () => {
|
|
10
|
+
expect(parseQueryNative('tag=a&tag=b&tag=c')).toEqual({ tag: ['a', 'b', 'c'] })
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('mixes scalars and arrays', () => {
|
|
14
|
+
expect(parseQueryNative('limit=10&tag=a&tag=b')).toEqual({ limit: '10', tag: ['a', 'b'] })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('empty string parses to an empty object', () => {
|
|
18
|
+
expect(parseQueryNative('')).toEqual({})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('decodes URL-encoded values (URLSearchParams semantics)', () => {
|
|
22
|
+
expect(parseQueryNative('q=hello%20world&plus=a+b')).toEqual({
|
|
23
|
+
q: 'hello world',
|
|
24
|
+
plus: 'a b',
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('key without a value yields an empty string', () => {
|
|
29
|
+
expect(parseQueryNative('flag')).toEqual({ flag: '' })
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('extractQuery', () => {
|
|
34
|
+
test('parses the query portion of a URL', () => {
|
|
35
|
+
expect(extractQuery('http://localhost/users?limit=10&tag=a', parseQueryNative)).toEqual({
|
|
36
|
+
limit: '10',
|
|
37
|
+
tag: 'a',
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('returns {} when the URL has no "?"', () => {
|
|
42
|
+
expect(extractQuery('http://localhost/users', parseQueryNative)).toEqual({})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('returns {} for a trailing "?" with an empty query', () => {
|
|
46
|
+
expect(extractQuery('http://localhost/users?', parseQueryNative)).toEqual({})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('custom parser receives the raw query string and its result passes through', () => {
|
|
50
|
+
let receivedRaw: string | undefined
|
|
51
|
+
const custom = (raw: string) => {
|
|
52
|
+
receivedRaw = raw
|
|
53
|
+
return { parsed: true }
|
|
54
|
+
}
|
|
55
|
+
const result = extractQuery('http://localhost/p?a=1&b=2', custom)
|
|
56
|
+
expect(receivedRaw).toBe('a=1&b=2')
|
|
57
|
+
expect(result).toEqual({ parsed: true })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('custom parser is NOT invoked when there is no query string', () => {
|
|
61
|
+
let called = false
|
|
62
|
+
const custom = (raw: string) => {
|
|
63
|
+
called = true
|
|
64
|
+
return { raw }
|
|
65
|
+
}
|
|
66
|
+
expect(extractQuery('http://localhost/p', custom)).toEqual({})
|
|
67
|
+
expect(extractQuery('http://localhost/p?', custom)).toEqual({})
|
|
68
|
+
expect(called).toBe(false)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Parses a raw query string into a params record. */
|
|
2
|
+
export type QueryParser = (queryString: string) => Record<string, unknown>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default query parser using URLSearchParams. Multi-value keys become arrays;
|
|
6
|
+
* single-value keys remain scalars (matches browser-native behavior).
|
|
7
|
+
*/
|
|
8
|
+
export function parseQueryNative(queryString: string): Record<string, unknown> {
|
|
9
|
+
const sp = new URLSearchParams(queryString)
|
|
10
|
+
const result: Record<string, unknown> = {}
|
|
11
|
+
for (const key of new Set(sp.keys())) {
|
|
12
|
+
const values = sp.getAll(key)
|
|
13
|
+
result[key] = values.length > 1 ? values : values[0]
|
|
14
|
+
}
|
|
15
|
+
return result
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Extracts and parses the query string portion of a URL (empty object when none). */
|
|
19
|
+
export function extractQuery(url: string, parser: QueryParser): Record<string, unknown> {
|
|
20
|
+
const q = url.indexOf('?')
|
|
21
|
+
if (q === -1) return {}
|
|
22
|
+
const raw = url.slice(q + 1)
|
|
23
|
+
return raw ? parser(raw) : {}
|
|
24
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, expect, it, test } from 'vitest'
|
|
2
|
+
import { SseEventSequencer, getSSEMeta, serializeSseValue, sse } from './sse.js'
|
|
3
|
+
|
|
4
|
+
describe('sse', () => {
|
|
5
|
+
test('returns the value unchanged (same reference)', () => {
|
|
6
|
+
const payload = { progress: 0.5 }
|
|
7
|
+
const result = sse(payload, { event: 'progress' })
|
|
8
|
+
expect(result).toBe(payload)
|
|
9
|
+
// Metadata rides in a WeakMap — never on the payload itself.
|
|
10
|
+
expect(Object.keys(result)).toEqual(['progress'])
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('getSSEMeta round-trips the attached options', () => {
|
|
14
|
+
const payload = sse({ progress: 0.5 }, { event: 'progress', id: 'p-1', retry: 3000 })
|
|
15
|
+
expect(getSSEMeta(payload)).toEqual({ event: 'progress', id: 'p-1', retry: 3000 })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('defaults to {} when no options are given', () => {
|
|
19
|
+
const payload = sse({ done: true })
|
|
20
|
+
expect(getSSEMeta(payload)).toEqual({})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('metadata is per-object — two payloads keep separate options', () => {
|
|
24
|
+
const a = sse({ n: 1 }, { event: 'a' })
|
|
25
|
+
const b = sse({ n: 2 }, { event: 'b' })
|
|
26
|
+
expect(getSSEMeta(a)).toEqual({ event: 'a' })
|
|
27
|
+
expect(getSSEMeta(b)).toEqual({ event: 'b' })
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe('getSSEMeta', () => {
|
|
32
|
+
test('returns undefined for plain (undecorated) objects', () => {
|
|
33
|
+
expect(getSSEMeta({ progress: 0.5 })).toBeUndefined()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('returns undefined for primitives', () => {
|
|
37
|
+
expect(getSSEMeta('text')).toBeUndefined()
|
|
38
|
+
expect(getSSEMeta(42)).toBeUndefined()
|
|
39
|
+
expect(getSSEMeta(true)).toBeUndefined()
|
|
40
|
+
expect(getSSEMeta(undefined)).toBeUndefined()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('returns undefined for null', () => {
|
|
44
|
+
expect(getSSEMeta(null)).toBeUndefined()
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('SseEventSequencer', () => {
|
|
49
|
+
function collector() {
|
|
50
|
+
const written: Array<{ data: string; event: string; id: string; retry?: number }> = []
|
|
51
|
+
return { written, write: (e: (typeof written)[number]) => void written.push(e) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function* gen(values: unknown[], ret?: unknown) {
|
|
55
|
+
for (const v of values) yield v
|
|
56
|
+
return ret
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
it('writes yields with the default event and auto-incrementing ids', async () => {
|
|
60
|
+
const { written, write } = collector()
|
|
61
|
+
await new SseEventSequencer(write, 'Watch').pump(gen([{ a: 1 }, { a: 2 }]))
|
|
62
|
+
expect(written).toEqual([
|
|
63
|
+
{ data: '{"a":1}', event: 'Watch', id: '0' },
|
|
64
|
+
{ data: '{"a":2}', event: 'Watch', id: '1' },
|
|
65
|
+
])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('sse() metadata overrides event/id/retry on yields', async () => {
|
|
69
|
+
const { written, write } = collector()
|
|
70
|
+
const tagged = sse({ p: 1 }, { event: 'progress', id: 'p-1', retry: 5 })
|
|
71
|
+
await new SseEventSequencer(write, 'Watch').pump(gen([tagged, { p: 2 }]))
|
|
72
|
+
expect(written[0]).toEqual({ data: '{"p":1}', event: 'progress', id: 'p-1', retry: 5 })
|
|
73
|
+
// counter not consumed by the custom id — next auto id is still 0
|
|
74
|
+
expect(written[1]).toEqual({ data: '{"p":2}', event: 'Watch', id: '0' })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('emits the return value as event: return and skips undefined returns', async () => {
|
|
78
|
+
const { written, write } = collector()
|
|
79
|
+
await new SseEventSequencer(write, 'W').pump(gen([1], { total: 1 }))
|
|
80
|
+
expect(written[1]).toEqual({ data: '{"total":1}', event: 'return', id: '1' })
|
|
81
|
+
|
|
82
|
+
const second = collector()
|
|
83
|
+
await new SseEventSequencer(second.write, 'W').pump(gen([1]))
|
|
84
|
+
expect(second.written).toHaveLength(1)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('writeError continues the id sequence and honors dispatched/meta events', async () => {
|
|
88
|
+
const { written, write } = collector()
|
|
89
|
+
const seq = new SseEventSequencer(write, 'W')
|
|
90
|
+
await seq.writeYield('x')
|
|
91
|
+
await seq.writeError({ data: { error: 'boom' }, sseEvent: 'error' })
|
|
92
|
+
expect(written[1]).toEqual({ data: '{"error":"boom"}', event: 'error', id: '1' })
|
|
93
|
+
|
|
94
|
+
await seq.writeError({ data: sse({ msg: 'm' }, { event: 'custom-fail' }) })
|
|
95
|
+
expect(written[2]!.event).toBe('custom-fail')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('serializeSseValue: strings pass through, null/undefined become empty', () => {
|
|
99
|
+
expect(serializeSseValue('raw')).toBe('raw')
|
|
100
|
+
expect(serializeSseValue(null)).toBe('')
|
|
101
|
+
expect(serializeSseValue(undefined)).toBe('')
|
|
102
|
+
expect(serializeSseValue(7)).toBe('7')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('propagates iterator errors to the caller (dispatch happens outside)', async () => {
|
|
106
|
+
const { write } = collector()
|
|
107
|
+
async function* boom() {
|
|
108
|
+
yield 1
|
|
109
|
+
throw new Error('mid')
|
|
110
|
+
}
|
|
111
|
+
await expect(new SseEventSequencer(write, 'W').pump(boom())).rejects.toThrow('mid')
|
|
112
|
+
})
|
|
113
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE metadata attachment, shared by every server adapter's stream handlers.
|
|
3
|
+
*/
|
|
4
|
+
export type SSEOptions = {
|
|
5
|
+
event?: string
|
|
6
|
+
id?: string
|
|
7
|
+
retry?: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const sseMetadata = new WeakMap<object, SSEOptions>()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Marks an object yield as an SSE event with custom metadata. Stream handlers
|
|
14
|
+
* read this metadata to set the SSE `event:`, `id:`, and `retry:` fields:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* yield sse({ progress: 0.5 }, { event: 'progress', id: 'p-1' })
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The value is returned unchanged — metadata rides in a WeakMap, never on
|
|
21
|
+
* the payload.
|
|
22
|
+
*/
|
|
23
|
+
export function sse<T extends object>(data: T, options?: SSEOptions): T {
|
|
24
|
+
sseMetadata.set(data, options ?? {})
|
|
25
|
+
return data
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Reads metadata attached via {@link sse}. Undefined for plain values. */
|
|
29
|
+
export function getSSEMeta(value: unknown): SSEOptions | undefined {
|
|
30
|
+
if (typeof value === 'object' && value !== null) {
|
|
31
|
+
return sseMetadata.get(value as object)
|
|
32
|
+
}
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** One wire-ready SSE event, as handed to the adapter's write function. */
|
|
37
|
+
export type SseEvent = {
|
|
38
|
+
data: string
|
|
39
|
+
event: string
|
|
40
|
+
id: string
|
|
41
|
+
retry?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Adapter-provided sink (e.g. Hono's `stream.writeSSE`). */
|
|
45
|
+
export type SseWrite = (event: SseEvent) => void | Promise<void>
|
|
46
|
+
|
|
47
|
+
/** Yield payloads: strings pass through, `null`/`undefined` become empty data. */
|
|
48
|
+
export function serializeSseValue(value: unknown): string {
|
|
49
|
+
if (typeof value === 'string') return value
|
|
50
|
+
return value != null ? JSON.stringify(value) : ''
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The SSE wire protocol shared by every stream handler: auto-incrementing
|
|
55
|
+
* event ids, `sse()` metadata overrides, the `event: 'return'` envelope for
|
|
56
|
+
* the generator's return value, and the error-event shape after mid-stream
|
|
57
|
+
* dispatch. Adapters supply only the framework write function.
|
|
58
|
+
*/
|
|
59
|
+
export class SseEventSequencer {
|
|
60
|
+
private eventId = 0
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
private readonly write: SseWrite,
|
|
64
|
+
/** Event name for yields without `sse()` metadata — the procedure name. */
|
|
65
|
+
private readonly defaultEvent: string,
|
|
66
|
+
) {}
|
|
67
|
+
|
|
68
|
+
/** Writes one yielded value; `sse()` metadata overrides event/id/retry. */
|
|
69
|
+
async writeYield(value: unknown): Promise<void> {
|
|
70
|
+
const meta = getSSEMeta(value)
|
|
71
|
+
await this.write({
|
|
72
|
+
data: serializeSseValue(value),
|
|
73
|
+
event: meta?.event ?? this.defaultEvent,
|
|
74
|
+
id: meta?.id ?? String(this.eventId++),
|
|
75
|
+
...(meta?.retry !== undefined && { retry: meta.retry }),
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Writes the generator's return value as the `event: 'return'` payload
|
|
81
|
+
* (clients resolve it via `TypedStream.result`). Skipped for `undefined` —
|
|
82
|
+
* a `void` generator produces no return event.
|
|
83
|
+
*/
|
|
84
|
+
async writeReturn(value: unknown): Promise<void> {
|
|
85
|
+
if (value === undefined) return
|
|
86
|
+
const data = typeof value === 'string' ? value : JSON.stringify(value)
|
|
87
|
+
await this.write({ data, event: 'return', id: String(this.eventId++) })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Writes a dispatched mid-stream error. `sse()` metadata on the payload
|
|
92
|
+
* (attachable inside `onMidStreamError`) wins over the dispatcher's event.
|
|
93
|
+
*/
|
|
94
|
+
async writeError(dispatched: { data: unknown; sseEvent?: string }): Promise<void> {
|
|
95
|
+
const meta = getSSEMeta(dispatched.data)
|
|
96
|
+
await this.write({
|
|
97
|
+
data: serializeSseValue(dispatched.data),
|
|
98
|
+
event: meta?.event ?? dispatched.sseEvent ?? 'error',
|
|
99
|
+
id: meta?.id ?? String(this.eventId++),
|
|
100
|
+
...(meta?.retry !== undefined && { retry: meta.retry }),
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Drives a complete stream: every yield, then the return event. Errors from
|
|
106
|
+
* the iterator propagate to the caller, which dispatches them and writes the
|
|
107
|
+
* result via {@link writeError} (the id sequence continues across the two).
|
|
108
|
+
*/
|
|
109
|
+
async pump(iterator: AsyncIterator<unknown, unknown>): Promise<void> {
|
|
110
|
+
let result = await iterator.next()
|
|
111
|
+
while (!result.done) {
|
|
112
|
+
await this.writeYield(result.value)
|
|
113
|
+
result = await iterator.next()
|
|
114
|
+
}
|
|
115
|
+
await this.writeReturn(result.value)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Transport-agnostic HTTP server types.
|
|
3
|
+
*
|
|
4
|
+
* The doc shapes here (`*RouteDoc`, `DocEnvelope`, `ErrorDoc`, `HeaderDoc`)
|
|
5
|
+
* are a FROZEN wire contract: they are the input to client codegen, and v9
|
|
6
|
+
* guarantees byte-identical generated output vs v8. Do not change field names,
|
|
7
|
+
* optionality, or nesting without re-validating the codegen goldens.
|
|
8
|
+
*/
|
|
9
|
+
import type { ProcedureKind } from '../core/types.js'
|
|
10
|
+
import type { ErrorTaxonomy } from './errors/taxonomy.js'
|
|
3
11
|
|
|
4
12
|
/**
|
|
5
13
|
* @typeParam TErrorKey - Union of valid taxonomy keys. Defaults to `string`
|
|
@@ -18,11 +26,6 @@ export interface RPCConfig<TErrorKey extends string = string> {
|
|
|
18
26
|
errors?: TErrorKey[]
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
export type FactoryItem<C> = {
|
|
22
|
-
factory: ReturnType<typeof Procedures<C, RPCConfig>>
|
|
23
|
-
factoryContext: (req: Request) => C
|
|
24
|
-
}
|
|
25
|
-
|
|
26
29
|
export interface RPCHttpRouteDoc extends RPCConfig {
|
|
27
30
|
kind: 'rpc'
|
|
28
31
|
name: string // procedure name
|
|
@@ -50,7 +53,7 @@ export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
|
|
|
50
53
|
* typo protection on `errors`.
|
|
51
54
|
*/
|
|
52
55
|
export interface APIConfig<TErrorKey extends string = string> {
|
|
53
|
-
/** HTTP route path (supports
|
|
56
|
+
/** HTTP route path (supports path params, e.g., '/users/:id') */
|
|
54
57
|
path: string
|
|
55
58
|
/** HTTP method for this endpoint */
|
|
56
59
|
method: HttpMethod
|
|
@@ -91,12 +94,12 @@ export interface APIHttpRouteDoc {
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
/**
|
|
94
|
-
* Constrains schema.
|
|
95
|
-
* Use with `satisfies`
|
|
97
|
+
* Constrains `schema.req` channel names to valid HTTP input sources.
|
|
98
|
+
* Use with `satisfies` to catch typos at compile time:
|
|
96
99
|
*
|
|
97
100
|
* @example
|
|
98
101
|
* schema: {
|
|
99
|
-
*
|
|
102
|
+
* req: {
|
|
100
103
|
* pathParams: Type.Object({ id: Type.String() }),
|
|
101
104
|
* qurey: Type.Object({ ... }), // TS error: 'qurey' not in APIInput
|
|
102
105
|
* } satisfies APIInput
|
|
@@ -184,8 +187,7 @@ export type ExtractConfig<TFactory> = TFactory extends {
|
|
|
184
187
|
export type ProceduresFactory = {
|
|
185
188
|
getProcedures: () => Array<{
|
|
186
189
|
name: string
|
|
187
|
-
kind?:
|
|
188
|
-
isStream?: boolean
|
|
190
|
+
kind?: ProcedureKind
|
|
189
191
|
config: any
|
|
190
192
|
handler: (ctx: any, params?: any) => Promise<any> | AsyncGenerator<any, any, unknown>
|
|
191
193
|
}>
|
|
@@ -205,9 +207,8 @@ export interface DocSource<T = AnyHttpRouteDoc> {
|
|
|
205
207
|
readonly docs: T[]
|
|
206
208
|
/**
|
|
207
209
|
* Optional list of procedures that were registered with this builder but
|
|
208
|
-
* couldn't be served by it
|
|
209
|
-
*
|
|
210
|
-
* `toJSON()` time so silently-dropped procedures don't slip through.
|
|
210
|
+
* couldn't be served by it. DocRegistry aggregates these across sources and
|
|
211
|
+
* warns at `toJSON()` time so silently-dropped procedures don't slip through.
|
|
211
212
|
*/
|
|
212
213
|
readonly skippedProcedures?: { name: string; reason: string }[]
|
|
213
214
|
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { Prettify, TSchemaLib } from './schema/types.js';
|
|
2
|
-
import type { HttpMethod, TBuilderConfig, TStreamContext, THttpStreamProcedureRegistration, TProcedureRegistration, TStreamProcedureRegistration, THttpProcedureRegistration } from './types.js';
|
|
3
|
-
export declare function makeCreateHttpStream<TContext>(procedures: Map<string, TProcedureRegistration<TContext, any> | TStreamProcedureRegistration<TContext, any> | THttpProcedureRegistration<TContext> | THttpStreamProcedureRegistration<TContext>>, builder?: {
|
|
4
|
-
config?: TBuilderConfig;
|
|
5
|
-
onCreate?: (procedure: any) => void;
|
|
6
|
-
}): <TName extends string, TReq extends Record<string, unknown> | undefined, TYieldType, TReturnType = void, TResHeaders = undefined, TErrorKey extends string = string>(name: TName, config: {
|
|
7
|
-
path: string;
|
|
8
|
-
method: HttpMethod;
|
|
9
|
-
scope?: string;
|
|
10
|
-
errors?: TErrorKey[];
|
|
11
|
-
description?: string;
|
|
12
|
-
schema: {
|
|
13
|
-
req?: TReq;
|
|
14
|
-
yield?: TYieldType;
|
|
15
|
-
returnType?: TReturnType;
|
|
16
|
-
res?: TResHeaders extends undefined ? undefined : {
|
|
17
|
-
headers: TResHeaders;
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
validateYields?: boolean;
|
|
21
|
-
}, handler: TResHeaders extends undefined ? (ctx: Prettify<TContext & TStreamContext>, req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]>; }> : undefined) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown> : (ctx: Prettify<TContext & TStreamContext>, req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]>; }> : undefined) => Promise<{
|
|
22
|
-
headers: TSchemaLib<TResHeaders>;
|
|
23
|
-
stream: AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>;
|
|
24
|
-
}>) => { [K in TName]: (ctx: Prettify<TContext>, req: TReq extends Record<string, unknown> ? Prettify<{ [K_1 in keyof TReq]: TSchemaLib<TReq[K_1]>; }> : undefined) => Promise<{
|
|
25
|
-
stream: AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>;
|
|
26
|
-
initialHeaders?: Record<string, string>;
|
|
27
|
-
}>; } & {
|
|
28
|
-
procedure: (ctx: Prettify<TContext>, req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]>; }> : undefined) => Promise<{
|
|
29
|
-
stream: AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>;
|
|
30
|
-
initialHeaders?: Record<string, string>;
|
|
31
|
-
}>;
|
|
32
|
-
info: {
|
|
33
|
-
path: string;
|
|
34
|
-
method: HttpMethod;
|
|
35
|
-
scope?: string;
|
|
36
|
-
errors?: string[];
|
|
37
|
-
description?: string;
|
|
38
|
-
schema?: {
|
|
39
|
-
req?: Record<string, import("./schema/types.js").TJSONSchema>;
|
|
40
|
-
res?: {
|
|
41
|
-
headers?: import("./schema/types.js").TJSONSchema;
|
|
42
|
-
};
|
|
43
|
-
yield?: import("./schema/types.js").TJSONSchema;
|
|
44
|
-
returnType?: import("./schema/types.js").TJSONSchema;
|
|
45
|
-
};
|
|
46
|
-
validation?: {
|
|
47
|
-
req?: Record<string, (value: any) => {
|
|
48
|
-
errors?: any[];
|
|
49
|
-
}>;
|
|
50
|
-
yield?: (value: any) => {
|
|
51
|
-
errors?: any[];
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
validateYields?: boolean;
|
|
55
|
-
name: TName;
|
|
56
|
-
kind: "http-stream";
|
|
57
|
-
};
|
|
58
|
-
};
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { ProcedureError, ProcedureRegistrationError, ProcedureValidationError, ProcedureYieldValidationError } from './errors.js';
|
|
2
|
-
import { computeSchema } from './schema/compute-schema.js';
|
|
3
|
-
import { captureDefinitionInfo } from './stack-utils.js';
|
|
4
|
-
import { checkPathParamConsistency } from './create-http.js';
|
|
5
|
-
/**
|
|
6
|
-
* Wraps a user's AsyncIterator in a generator that:
|
|
7
|
-
* - optionally validates each yielded value
|
|
8
|
-
* - augments error stack traces with the procedure definition site
|
|
9
|
-
* - aborts the stream controller on completion
|
|
10
|
-
*/
|
|
11
|
-
async function* yieldingWrapper(userIterator, validateYields, validations, abortController, definitionInfo, name) {
|
|
12
|
-
try {
|
|
13
|
-
let result = await userIterator.next();
|
|
14
|
-
while (!result.done) {
|
|
15
|
-
const value = result.value;
|
|
16
|
-
if (validateYields && validations.yield) {
|
|
17
|
-
const { errors } = validations.yield(value);
|
|
18
|
-
if (errors)
|
|
19
|
-
throw new ProcedureYieldValidationError(name, `Yield validation error for ${name}`, errors, definitionInfo);
|
|
20
|
-
}
|
|
21
|
-
yield value;
|
|
22
|
-
result = await userIterator.next();
|
|
23
|
-
}
|
|
24
|
-
return result.value;
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
if (definitionInfo.definedAt && error && typeof error.stack === 'string') {
|
|
28
|
-
const { file, line, column } = definitionInfo.definedAt;
|
|
29
|
-
error.stack = `${error.stack}\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`;
|
|
30
|
-
}
|
|
31
|
-
throw error;
|
|
32
|
-
}
|
|
33
|
-
finally {
|
|
34
|
-
try {
|
|
35
|
-
await userIterator.return?.(undefined);
|
|
36
|
-
}
|
|
37
|
-
catch { /* swallow */ }
|
|
38
|
-
abortController.abort('stream-completed');
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export function makeCreateHttpStream(procedures, builder) {
|
|
42
|
-
return function CreateHttpStream(name, config, handler) {
|
|
43
|
-
const definitionInfo = captureDefinitionInfo();
|
|
44
|
-
if (procedures.has(name))
|
|
45
|
-
throw new Error(`Procedure with name ${name} is already registered`);
|
|
46
|
-
if (config.schema?.params) {
|
|
47
|
-
throw new ProcedureRegistrationError(name, `Use schema.req.body (or schema.req.query) instead of schema.params on CreateHttpStream. Procedure: "${name}".`, definitionInfo);
|
|
48
|
-
}
|
|
49
|
-
const { jsonSchema, validations } = computeSchema(name, {
|
|
50
|
-
req: config.schema.req,
|
|
51
|
-
yieldType: config.schema.yield,
|
|
52
|
-
returnType: config.schema.returnType,
|
|
53
|
-
}, definitionInfo);
|
|
54
|
-
const pathParamsSchema = jsonSchema.req?.pathParams;
|
|
55
|
-
checkPathParamConsistency(name, config.path, pathParamsSchema, definitionInfo);
|
|
56
|
-
const errorFactory = (message, meta) => new ProcedureError(name, message, meta, definitionInfo);
|
|
57
|
-
const validateYields = config.validateYields ?? false;
|
|
58
|
-
const wrappedHandler = async (ctx, req) => {
|
|
59
|
-
const abortController = new AbortController();
|
|
60
|
-
const skipValidation = ctx.isPrevalidated || builder?.config?.noRuntimeValidation;
|
|
61
|
-
if (validations?.req && !skipValidation) {
|
|
62
|
-
for (const [channel, validator] of Object.entries(validations.req)) {
|
|
63
|
-
const channelValue = req?.[channel];
|
|
64
|
-
const { errors } = validator(channelValue);
|
|
65
|
-
if (errors) {
|
|
66
|
-
throw new ProcedureValidationError(name, `Validation error for ${name} in req.${channel}`, errors, definitionInfo);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const incomingSignal = ctx.signal;
|
|
71
|
-
const signal = incomingSignal ? AbortSignal.any([incomingSignal, abortController.signal]) : abortController.signal;
|
|
72
|
-
const streamCtx = { error: errorFactory, signal };
|
|
73
|
-
const handlerResult = handler({ ...ctx, ...streamCtx }, req);
|
|
74
|
-
if (typeof handlerResult?.[Symbol.asyncIterator] === 'function') {
|
|
75
|
-
// Plain async generator from user
|
|
76
|
-
const userIterator = handlerResult[Symbol.asyncIterator]();
|
|
77
|
-
return { stream: yieldingWrapper(userIterator, validateYields, validations, abortController, definitionInfo, name) };
|
|
78
|
-
}
|
|
79
|
-
else if (typeof handlerResult?.then === 'function') {
|
|
80
|
-
// Async preamble: Promise<{ headers, stream }>
|
|
81
|
-
const resolved = await handlerResult;
|
|
82
|
-
if (!resolved || typeof resolved !== 'object' || !('stream' in resolved)) {
|
|
83
|
-
throw new ProcedureError(name, `CreateHttpStream handler returned a Promise that did not resolve to { headers, stream }. Got: ${JSON.stringify(resolved)}`, undefined, definitionInfo);
|
|
84
|
-
}
|
|
85
|
-
const userIterator = resolved.stream[Symbol.asyncIterator]();
|
|
86
|
-
return {
|
|
87
|
-
stream: yieldingWrapper(userIterator, validateYields, validations, abortController, definitionInfo, name),
|
|
88
|
-
initialHeaders: resolved.headers,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
throw new ProcedureError(name, `CreateHttpStream handler must return an AsyncGenerator or Promise<{ headers, stream }>.`, undefined, definitionInfo);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
const registeredProcedure = {
|
|
96
|
-
name,
|
|
97
|
-
kind: 'http-stream',
|
|
98
|
-
config: {
|
|
99
|
-
path: config.path,
|
|
100
|
-
method: config.method,
|
|
101
|
-
scope: config.scope,
|
|
102
|
-
errors: config.errors,
|
|
103
|
-
description: config.description,
|
|
104
|
-
schema: jsonSchema,
|
|
105
|
-
validation: { req: validations.req, yield: validations.yield },
|
|
106
|
-
validateYields,
|
|
107
|
-
},
|
|
108
|
-
handler: wrappedHandler,
|
|
109
|
-
};
|
|
110
|
-
procedures.set(name, registeredProcedure);
|
|
111
|
-
builder?.onCreate?.(registeredProcedure);
|
|
112
|
-
const info = { name, kind: 'http-stream', ...registeredProcedure.config };
|
|
113
|
-
// Explicit return type — preserves TReq/TYieldType/TReturnType through the public callable.
|
|
114
|
-
// Do NOT use `typeof registeredProcedure.handler` — that erases generics to `any`.
|
|
115
|
-
return {
|
|
116
|
-
[name]: registeredProcedure.handler,
|
|
117
|
-
procedure: registeredProcedure.handler,
|
|
118
|
-
info,
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
//# sourceMappingURL=create-http-stream.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-http-stream.js","sourceRoot":"","sources":["../src/create-http-stream.ts"],"names":[],"mappings":"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"}
|