ts-procedures 8.5.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -101
- package/agent_config/claude-code/.claude-plugin/plugin.json +1 -1
- package/agent_config/claude-code/agents/ts-procedures-architect.md +11 -10
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +25 -12
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +10 -12
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +141 -45
- package/agent_config/claude-code/skills/ts-procedures/checklist.md +7 -6
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +45 -6
- package/agent_config/claude-code/skills/ts-procedures/templates/client.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures/templates/hono.md +1 -1
- package/agent_config/copilot/copilot-instructions.md +50 -33
- package/agent_config/cursor/cursorrules +50 -33
- package/build/adapters/astro/astro-context.js.map +1 -0
- package/build/adapters/astro/create-handler.js.map +1 -0
- package/build/adapters/astro/index.js.map +1 -0
- package/build/{implementations/http → adapters}/astro/index.test.js +1 -1
- package/build/adapters/astro/index.test.js.map +1 -0
- package/build/adapters/astro/rewrite-request.js.map +1 -0
- package/build/adapters/hono/envelope-parity.test.js +98 -0
- package/build/adapters/hono/envelope-parity.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http-stream.d.ts +1 -1
- package/build/adapters/hono/handlers/http-stream.js +55 -0
- package/build/adapters/hono/handlers/http-stream.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.js +1 -1
- package/build/adapters/hono/handlers/http-stream.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http.d.ts +1 -1
- package/build/adapters/hono/handlers/http.js +50 -0
- package/build/adapters/hono/handlers/http.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/http.test.js +1 -1
- package/build/adapters/hono/handlers/http.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/rpc.d.ts +2 -2
- package/build/adapters/hono/handlers/rpc.js +23 -0
- package/build/adapters/hono/handlers/rpc.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/rpc.test.js +1 -1
- package/build/adapters/hono/handlers/rpc.test.js.map +1 -0
- package/build/adapters/hono/handlers/stream.d.ts +12 -0
- package/build/adapters/hono/handlers/stream.js +89 -0
- package/build/adapters/hono/handlers/stream.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/handlers/stream.test.js +3 -2
- package/build/adapters/hono/handlers/stream.test.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/index.d.ts +24 -12
- package/build/{implementations/http → adapters}/hono/index.js +19 -8
- package/build/adapters/hono/index.js.map +1 -0
- package/build/{implementations/http → adapters}/hono/index.test.js +2 -4
- package/build/adapters/hono/index.test.js.map +1 -0
- package/build/{implementations/http → adapters/hono}/on-request-error.test.js +2 -2
- package/build/adapters/hono/on-request-error.test.js.map +1 -0
- package/build/adapters/hono/request.d.ts +7 -0
- package/build/adapters/hono/request.js +22 -0
- package/build/adapters/hono/request.js.map +1 -0
- package/build/{implementations/http → adapters/hono}/route-errors.test.js +4 -4
- package/build/adapters/hono/route-errors.test.js.map +1 -0
- package/build/adapters/hono/types.d.ts +55 -0
- package/build/adapters/hono/types.js +19 -0
- package/build/adapters/hono/types.js.map +1 -0
- package/build/client/freeze.test.js +39 -0
- package/build/client/freeze.test.js.map +1 -0
- package/build/client/typed-error-dispatch.test.js +2 -2
- package/build/client/typed-error-dispatch.test.js.map +1 -1
- package/build/codegen/__fixtures__/make-envelope.d.ts +1 -1
- package/build/codegen/bin/cli.d.ts +5 -0
- package/build/codegen/bin/cli.js +139 -182
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/bin/cli.test.js +12 -2
- package/build/codegen/bin/cli.test.js.map +1 -1
- package/build/codegen/bin/flag-specs.d.ts +9 -0
- package/build/codegen/bin/flag-specs.js +33 -31
- package/build/codegen/bin/flag-specs.js.map +1 -1
- package/build/codegen/bin/flag-specs.test.js +14 -1
- package/build/codegen/bin/flag-specs.test.js.map +1 -1
- package/build/codegen/collect-models.d.ts +1 -1
- package/build/codegen/emit/api-route.d.ts +8 -0
- package/build/codegen/emit/api-route.js +156 -0
- package/build/codegen/emit/api-route.js.map +1 -0
- package/build/codegen/emit/context.d.ts +30 -0
- package/build/codegen/emit/context.js +2 -0
- package/build/codegen/emit/context.js.map +1 -0
- package/build/codegen/emit/declarations.d.ts +24 -0
- package/build/codegen/emit/declarations.js +48 -0
- package/build/codegen/emit/declarations.js.map +1 -0
- package/build/codegen/emit/format-types.d.ts +61 -0
- package/build/codegen/emit/format-types.js +188 -0
- package/build/codegen/emit/format-types.js.map +1 -0
- package/build/codegen/emit/http-stream-route.d.ts +7 -0
- package/build/codegen/emit/http-stream-route.js +138 -0
- package/build/codegen/emit/http-stream-route.js.map +1 -0
- package/build/codegen/emit/route-shared.d.ts +35 -0
- package/build/codegen/emit/route-shared.js +88 -0
- package/build/codegen/emit/route-shared.js.map +1 -0
- package/build/codegen/emit/rpc-route.d.ts +7 -0
- package/build/codegen/emit/rpc-route.js +37 -0
- package/build/codegen/emit/rpc-route.js.map +1 -0
- package/build/codegen/emit/scope-file.d.ts +39 -0
- package/build/codegen/emit/scope-file.js +166 -0
- package/build/codegen/emit/scope-file.js.map +1 -0
- package/build/codegen/emit/stream-route.d.ts +7 -0
- package/build/codegen/emit/stream-route.js +62 -0
- package/build/codegen/emit/stream-route.js.map +1 -0
- package/build/codegen/emit-errors.d.ts +1 -1
- package/build/codegen/emit-errors.integration.test.js +1 -1
- package/build/codegen/emit-errors.integration.test.js.map +1 -1
- package/build/codegen/emit-index.js +13 -0
- package/build/codegen/emit-index.js.map +1 -1
- package/build/codegen/emit-index.test.js +25 -0
- package/build/codegen/emit-index.test.js.map +1 -1
- package/build/codegen/emit-scope.d.ts +13 -30
- package/build/codegen/emit-scope.js +15 -807
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +86 -4
- package/build/codegen/emit-scope.test.js.map +1 -1
- package/build/codegen/goldens.test.js +69 -0
- package/build/codegen/goldens.test.js.map +1 -0
- package/build/codegen/group-routes.d.ts +1 -1
- package/build/codegen/pipeline.d.ts +1 -1
- package/build/codegen/resolve-envelope.d.ts +1 -1
- package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
- package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
- package/build/codegen/targets/_shared/target-run.d.ts +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
- package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
- package/build/core/create-http-stream.d.ts +50 -0
- package/build/core/create-http-stream.js +108 -0
- package/build/core/create-http-stream.js.map +1 -0
- package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
- package/build/core/create-http-stream.test.js.map +1 -0
- package/build/core/create-http.d.ts +51 -0
- package/build/core/create-http.js +65 -0
- package/build/core/create-http.js.map +1 -0
- package/build/{create-http.test.js → core/create-http.test.js} +13 -4
- package/build/core/create-http.test.js.map +1 -0
- package/build/core/create-stream.d.ts +26 -0
- package/build/core/create-stream.js +80 -0
- package/build/core/create-stream.js.map +1 -0
- package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
- package/build/core/create-stream.test.js.map +1 -0
- package/build/core/create.d.ts +22 -0
- package/build/core/create.js +71 -0
- package/build/core/create.js.map +1 -0
- package/build/{create.test.js → core/create.test.js} +25 -46
- package/build/core/create.test.js.map +1 -0
- package/build/core/definition-site.d.ts +24 -0
- package/build/{stack-utils.js → core/definition-site.js} +20 -20
- package/build/core/definition-site.js.map +1 -0
- package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
- package/build/core/definition-site.test.js.map +1 -0
- package/build/{errors.d.ts → core/errors.d.ts} +19 -8
- package/build/{errors.js → core/errors.js} +21 -26
- package/build/core/errors.js.map +1 -0
- package/build/core/errors.test.js.map +1 -0
- package/build/core/factory-options.test.js +82 -0
- package/build/core/factory-options.test.js.map +1 -0
- package/build/core/http-route.d.ts +13 -0
- package/build/core/http-route.js +54 -0
- package/build/core/http-route.js.map +1 -0
- package/build/core/internal.d.ts +72 -0
- package/build/core/internal.js +128 -0
- package/build/core/internal.js.map +1 -0
- package/build/{migration.test.js → core/migration.test.js} +17 -1
- package/build/core/migration.test.js.map +1 -0
- package/build/core/procedures.d.ts +143 -0
- package/build/core/procedures.js +64 -0
- package/build/core/procedures.js.map +1 -0
- package/build/{index.test.js → core/procedures.test.js} +14 -11
- package/build/core/procedures.test.js.map +1 -0
- package/build/core/types.d.ts +182 -0
- package/build/{schema → core}/types.js.map +1 -1
- package/build/exports.d.ts +31 -11
- package/build/exports.js +23 -8
- package/build/exports.js.map +1 -1
- package/build/schema/adapter.d.ts +35 -0
- package/build/schema/adapter.js +13 -0
- package/build/schema/adapter.js.map +1 -0
- package/build/schema/adapter.test.js +53 -0
- package/build/schema/adapter.test.js.map +1 -0
- package/build/schema/compile.d.ts +37 -0
- package/build/schema/compile.js +38 -0
- package/build/schema/compile.js.map +1 -0
- package/build/schema/compile.test.js +78 -0
- package/build/schema/compile.test.js.map +1 -0
- package/build/schema/compute-schema.d.ts +47 -37
- package/build/schema/compute-schema.js +86 -29
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/compute-schema.test.js +158 -40
- package/build/schema/compute-schema.test.js.map +1 -1
- package/build/schema/json-schema.d.ts +17 -0
- package/build/schema/json-schema.js +2 -0
- package/build/schema/json-schema.js.map +1 -0
- package/build/schema/typebox.d.ts +11 -0
- package/build/schema/typebox.js +24 -0
- package/build/schema/typebox.js.map +1 -0
- package/build/schema/typebox.test.js +34 -0
- package/build/schema/typebox.test.js.map +1 -0
- package/build/server/context.d.ts +8 -0
- package/build/server/context.js +7 -0
- package/build/server/context.js.map +1 -0
- package/build/server/context.test.js +16 -0
- package/build/server/context.test.js.map +1 -0
- package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
- package/build/server/doc-envelope.js.map +1 -0
- package/build/server/doc-envelope.test.d.ts +1 -0
- package/build/server/doc-envelope.test.js.map +1 -0
- package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
- package/build/{implementations/http → server}/doc-registry.js +9 -5
- package/build/server/doc-registry.js.map +1 -0
- package/build/server/doc-registry.test.d.ts +1 -0
- package/build/{implementations/http → server}/doc-registry.test.js +27 -24
- package/build/server/doc-registry.test.js.map +1 -0
- package/build/server/docs/docs.test.d.ts +1 -0
- package/build/server/docs/docs.test.js +237 -0
- package/build/server/docs/docs.test.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
- package/build/server/docs/http-doc.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
- package/build/server/docs/http-stream-doc.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
- package/build/server/docs/rpc-doc.js.map +1 -0
- package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
- package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
- package/build/server/docs/stream-doc.js.map +1 -0
- package/build/server/errors/dispatch.d.ts +96 -0
- package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
- package/build/server/errors/dispatch.js.map +1 -0
- package/build/server/errors/dispatch.test.d.ts +1 -0
- package/build/server/errors/dispatch.test.js +418 -0
- package/build/server/errors/dispatch.test.js.map +1 -0
- package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
- package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
- package/build/server/errors/taxonomy.js.map +1 -0
- package/build/server/errors/taxonomy.test.d.ts +1 -0
- package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
- package/build/server/errors/taxonomy.test.js.map +1 -0
- package/build/server/index.d.ts +29 -0
- package/build/server/index.js +27 -0
- package/build/server/index.js.map +1 -0
- package/build/server/no-framework-imports.test.d.ts +1 -0
- package/build/server/no-framework-imports.test.js +40 -0
- package/build/server/no-framework-imports.test.js.map +1 -0
- package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
- package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
- package/build/server/paths.js.map +1 -0
- package/build/server/paths.test.d.ts +1 -0
- package/build/server/paths.test.js +111 -0
- package/build/server/paths.test.js.map +1 -0
- package/build/server/request/params.d.ts +29 -0
- package/build/server/request/params.js +43 -0
- package/build/server/request/params.js.map +1 -0
- package/build/server/request/params.test.d.ts +1 -0
- package/build/server/request/params.test.js +91 -0
- package/build/server/request/params.test.js.map +1 -0
- package/build/server/request/query.d.ts +9 -0
- package/build/server/request/query.js +22 -0
- package/build/server/request/query.js.map +1 -0
- package/build/server/request/query.test.d.ts +1 -0
- package/build/server/request/query.test.js +60 -0
- package/build/server/request/query.test.js.map +1 -0
- package/build/server/sse.d.ts +70 -0
- package/build/server/sse.js +94 -0
- package/build/server/sse.js.map +1 -0
- package/build/server/sse.test.d.ts +1 -0
- package/build/server/sse.test.js +98 -0
- package/build/server/sse.test.js.map +1 -0
- package/build/{implementations → server}/types.d.ts +17 -15
- package/build/{implementations → server}/types.js.map +1 -1
- package/docs/astro-adapter.md +8 -9
- package/docs/client-and-codegen.md +4 -4
- package/docs/client-error-handling.md +92 -5
- package/docs/codegen-kotlin.md +2 -3
- package/docs/codegen-swift.md +1 -2
- package/docs/core.md +135 -54
- package/docs/http-integrations.md +83 -6
- package/docs/migration-v8-to-v9.md +192 -0
- package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
- package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
- package/docs/streaming.md +12 -0
- package/package.json +23 -47
- package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
- package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
- package/src/adapters/hono/envelope-parity.test.ts +126 -0
- package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
- package/src/adapters/hono/handlers/http-stream.ts +73 -0
- package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
- package/src/adapters/hono/handlers/http.ts +70 -0
- package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
- package/src/adapters/hono/handlers/rpc.ts +39 -0
- package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
- package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
- package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
- package/src/{implementations/http → adapters}/hono/index.ts +35 -30
- package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
- package/src/adapters/hono/request.ts +28 -0
- package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
- package/src/{implementations/http → adapters}/hono/types.ts +43 -20
- package/src/client/freeze.test.ts +41 -0
- package/src/client/typed-error-dispatch.test.ts +3 -3
- package/src/codegen/__fixtures__/make-envelope.ts +1 -1
- package/src/codegen/__fixtures__/models-envelope.json +310 -0
- package/src/codegen/__fixtures__/users-envelope.json +9 -0
- package/src/codegen/__goldens__/MANIFEST.json +85 -0
- package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
- package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
- package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
- package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
- package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
- package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
- package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
- package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
- package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
- package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
- package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
- package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
- package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
- package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
- package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
- package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
- package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
- package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
- package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
- package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
- package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
- package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
- package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
- package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
- package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
- package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
- package/src/codegen/bin/cli.test.ts +13 -2
- package/src/codegen/bin/cli.ts +181 -144
- package/src/codegen/bin/flag-specs.test.ts +16 -1
- package/src/codegen/bin/flag-specs.ts +43 -31
- package/src/codegen/bundle-size.test.ts +1 -1
- package/src/codegen/collect-models.ts +1 -1
- package/src/codegen/e2e.test.ts +1 -1
- package/src/codegen/emit/api-route.ts +184 -0
- package/src/codegen/emit/context.ts +32 -0
- package/src/codegen/emit/declarations.ts +49 -0
- package/src/codegen/emit/format-types.ts +232 -0
- package/src/codegen/emit/http-stream-route.ts +162 -0
- package/src/codegen/emit/route-shared.ts +102 -0
- package/src/codegen/emit/rpc-route.ts +49 -0
- package/src/codegen/emit/scope-file.ts +226 -0
- package/src/codegen/emit/stream-route.ts +81 -0
- package/src/codegen/emit-errors.integration.test.ts +2 -2
- package/src/codegen/emit-errors.test.ts +1 -1
- package/src/codegen/emit-errors.ts +1 -1
- package/src/codegen/emit-index.test.ts +34 -0
- package/src/codegen/emit-index.ts +19 -0
- package/src/codegen/emit-scope.test.ts +96 -6
- package/src/codegen/emit-scope.ts +15 -1003
- package/src/codegen/goldens.test.ts +89 -0
- package/src/codegen/group-routes.test.ts +1 -1
- package/src/codegen/group-routes.ts +1 -1
- package/src/codegen/pipeline.test.ts +1 -1
- package/src/codegen/pipeline.ts +1 -1
- package/src/codegen/resolve-envelope.test.ts +1 -1
- package/src/codegen/resolve-envelope.ts +1 -1
- package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
- package/src/codegen/targets/_shared/error-schemas.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.ts +1 -1
- package/src/codegen/targets/_shared/target-run.ts +1 -1
- package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +6 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
- package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
- package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
- package/src/codegen/targets/swift/__fixtures__/users-golden.swift +6 -0
- package/src/codegen/targets/swift/access-level.test.ts +1 -1
- package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
- package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
- package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
- package/src/codegen/targets/ts/shared-models.test.ts +1 -1
- package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
- package/src/core/create-http-stream.ts +207 -0
- package/src/{create-http.test.ts → core/create-http.test.ts} +15 -4
- package/src/core/create-http.ts +126 -0
- package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
- package/src/core/create-stream.ts +142 -0
- package/src/{create.test.ts → core/create.test.ts} +25 -57
- package/src/core/create.ts +121 -0
- package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
- package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
- package/src/{errors.test.ts → core/errors.test.ts} +1 -1
- package/src/{errors.ts → core/errors.ts} +30 -28
- package/src/core/factory-options.test.ts +112 -0
- package/src/core/http-route.ts +73 -0
- package/src/core/internal.ts +203 -0
- package/src/{migration.test.ts → core/migration.test.ts} +23 -1
- package/src/{index.test.ts → core/procedures.test.ts} +13 -11
- package/src/core/procedures.ts +75 -0
- package/src/core/types.ts +195 -0
- package/src/exports.ts +60 -11
- package/src/schema/adapter.test.ts +58 -0
- package/src/schema/adapter.ts +45 -0
- package/src/schema/compile.test.ts +95 -0
- package/src/schema/compile.ts +64 -0
- package/src/schema/compute-schema.test.ts +222 -41
- package/src/schema/compute-schema.ts +145 -71
- package/src/schema/json-schema.ts +21 -0
- package/src/schema/typebox.test.ts +40 -0
- package/src/schema/typebox.ts +27 -0
- package/src/server/context.test.ts +22 -0
- package/src/server/context.ts +18 -0
- package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
- package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
- package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
- package/src/{implementations/http → server}/doc-registry.ts +11 -7
- package/src/server/docs/docs.test.ts +287 -0
- package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
- package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
- package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
- package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
- package/src/server/errors/dispatch.test.ts +450 -0
- package/src/server/errors/dispatch.ts +189 -0
- package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
- package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
- package/src/server/index.ts +29 -0
- package/src/server/no-framework-imports.test.ts +43 -0
- package/src/server/paths.test.ts +141 -0
- package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
- package/src/server/request/params.test.ts +143 -0
- package/src/server/request/params.ts +68 -0
- package/src/server/request/query.test.ts +70 -0
- package/src/server/request/query.ts +24 -0
- package/src/server/sse.test.ts +113 -0
- package/src/server/sse.ts +117 -0
- package/src/{implementations → server}/types.ts +17 -16
- package/build/create-http-stream.d.ts +0 -58
- package/build/create-http-stream.js +0 -122
- package/build/create-http-stream.js.map +0 -1
- package/build/create-http-stream.test.js.map +0 -1
- package/build/create-http.d.ts +0 -49
- package/build/create-http.js +0 -108
- package/build/create-http.js.map +0 -1
- package/build/create-http.test.js.map +0 -1
- package/build/create-stream.d.ts +0 -35
- package/build/create-stream.js +0 -123
- package/build/create-stream.js.map +0 -1
- package/build/create-stream.test.js.map +0 -1
- package/build/create.d.ts +0 -28
- package/build/create.js +0 -82
- package/build/create.js.map +0 -1
- package/build/create.test.js.map +0 -1
- package/build/doc-envelope.js.map +0 -1
- package/build/doc-envelope.test.js.map +0 -1
- package/build/errors.js.map +0 -1
- package/build/errors.test.js.map +0 -1
- package/build/implementations/http/astro/astro-context.js.map +0 -1
- package/build/implementations/http/astro/create-handler.js.map +0 -1
- package/build/implementations/http/astro/index.js.map +0 -1
- package/build/implementations/http/astro/index.test.js.map +0 -1
- package/build/implementations/http/astro/rewrite-request.js.map +0 -1
- package/build/implementations/http/doc-registry.js.map +0 -1
- package/build/implementations/http/doc-registry.test.js.map +0 -1
- package/build/implementations/http/error-dispatch.d.ts +0 -76
- package/build/implementations/http/error-dispatch.js.map +0 -1
- package/build/implementations/http/error-dispatch.test.js +0 -254
- package/build/implementations/http/error-dispatch.test.js.map +0 -1
- package/build/implementations/http/error-taxonomy.js.map +0 -1
- package/build/implementations/http/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
- package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
- package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
- package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
- package/build/implementations/http/hono/handlers/http-stream.js +0 -123
- package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
- package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
- package/build/implementations/http/hono/handlers/http.js +0 -110
- package/build/implementations/http/hono/handlers/http.js.map +0 -1
- package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
- package/build/implementations/http/hono/handlers/rpc.js +0 -32
- package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
- package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
- package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
- package/build/implementations/http/hono/handlers/stream.js +0 -147
- package/build/implementations/http/hono/handlers/stream.js.map +0 -1
- package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
- package/build/implementations/http/hono/index.js.map +0 -1
- package/build/implementations/http/hono/index.test.js.map +0 -1
- package/build/implementations/http/hono/path.js.map +0 -1
- package/build/implementations/http/hono/path.test.js +0 -83
- package/build/implementations/http/hono/path.test.js.map +0 -1
- package/build/implementations/http/hono/types.d.ts +0 -51
- package/build/implementations/http/hono/types.js.map +0 -1
- package/build/implementations/http/on-request-error.test.js.map +0 -1
- package/build/implementations/http/route-errors.test.js.map +0 -1
- package/build/index.d.ts +0 -175
- package/build/index.js +0 -47
- package/build/index.js.map +0 -1
- package/build/index.test.js.map +0 -1
- package/build/migration.test.js.map +0 -1
- package/build/schema/extract-json-schema.d.ts +0 -2
- package/build/schema/extract-json-schema.js +0 -12
- package/build/schema/extract-json-schema.js.map +0 -1
- package/build/schema/extract-json-schema.test.js +0 -23
- package/build/schema/extract-json-schema.test.js.map +0 -1
- package/build/schema/parser.d.ts +0 -36
- package/build/schema/parser.js +0 -210
- package/build/schema/parser.js.map +0 -1
- package/build/schema/parser.test.js +0 -120
- package/build/schema/parser.test.js.map +0 -1
- package/build/schema/resolve-schema-lib.d.ts +0 -12
- package/build/schema/resolve-schema-lib.js +0 -11
- package/build/schema/resolve-schema-lib.js.map +0 -1
- package/build/schema/resolve-schema-lib.test.js +0 -17
- package/build/schema/resolve-schema-lib.test.js.map +0 -1
- package/build/schema/types.d.ts +0 -8
- package/build/schema/types.js +0 -2
- package/build/stack-utils.d.ts +0 -25
- package/build/stack-utils.js.map +0 -1
- package/build/stack-utils.test.js.map +0 -1
- package/build/types.d.ts +0 -142
- package/build/types.js +0 -2
- package/build/types.js.map +0 -1
- package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
- package/docs/handoffs/ajsc-named-type-collision.md +0 -134
- package/docs/handoffs/ajsc-named-type-support.md +0 -181
- package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
- package/docs/npm-workspaces-migration-plan.md +0 -611
- package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
- package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
- package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
- package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
- package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
- package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
- package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
- package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
- package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
- package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
- package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
- package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
- package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
- package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
- package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
- package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
- package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
- package/src/create-http-stream.ts +0 -191
- package/src/create-http.ts +0 -210
- package/src/create-stream.ts +0 -228
- package/src/create.ts +0 -172
- package/src/implementations/http/README.md +0 -390
- package/src/implementations/http/error-dispatch.test.ts +0 -283
- package/src/implementations/http/error-dispatch.ts +0 -176
- package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
- package/src/implementations/http/hono/handlers/http.ts +0 -145
- package/src/implementations/http/hono/handlers/rpc.ts +0 -54
- package/src/implementations/http/hono/path.test.ts +0 -96
- package/src/index.ts +0 -101
- package/src/schema/extract-json-schema.test.ts +0 -25
- package/src/schema/extract-json-schema.ts +0 -15
- package/src/schema/parser.test.ts +0 -182
- package/src/schema/parser.ts +0 -265
- package/src/schema/resolve-schema-lib.test.ts +0 -19
- package/src/schema/resolve-schema-lib.ts +0 -29
- package/src/schema/types.ts +0 -20
- package/src/types.ts +0 -133
- /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
- /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
- /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/index.js +0 -0
- /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
- /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
- /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
- /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
- /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
- /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
- /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
- /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
- /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
- /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
- /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
- /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
- /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
- /package/build/{errors.test.js → core/errors.test.js} +0 -0
- /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
- /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
- /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
- /package/build/{implementations/http/hono → core}/types.js +0 -0
- /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
- /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
- /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
- /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
- /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
- /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
- /package/build/{implementations → server}/types.js +0 -0
- /package/src/{implementations/http → adapters}/astro/README.md +0 -0
- /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
- /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
- /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
- /package/src/{implementations/http → adapters}/astro/rewrite-request.ts +0 -0
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
# DX Feedback Round — Design
|
|
2
|
-
|
|
3
|
-
**Date:** 2026-06-05
|
|
4
|
-
**Status:** Approved (pending spec review)
|
|
5
|
-
**Source:** Downstream-developer feedback from building the demo server (`src/server`), 5 findings.
|
|
6
|
-
|
|
7
|
-
This round addresses five findings. Each section states the problem, the decision
|
|
8
|
-
taken (with the alternatives considered), and the concrete shape of the change.
|
|
9
|
-
|
|
10
|
-
Two items are cross-logged but **explicitly out of scope** this round (no scope creep):
|
|
11
|
-
- The `ts-channels` codegen has the identical #3 (per-route inlining) defect — coordinating the two is follow-up.
|
|
12
|
-
- #5 is cross-logged with `ts-channels #9` / `mvc-kit #2` — aligning all three scaffolders is follow-up.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## #1 — `ts-procedures-codegen --help` (MEDIUM)
|
|
17
|
-
|
|
18
|
-
### Problem
|
|
19
|
-
`--help` falls through to the unknown-flag branch and exits non-zero with a
|
|
20
|
-
misfiring did-you-mean suggestion (`Did you mean --url?`). There is no usage text
|
|
21
|
-
anywhere in the CLI, so the rich flag surface (`--watch`, `--dry-run`,
|
|
22
|
-
`--enum-style`, Kotlin/Swift targets, …) is undiscoverable without reading the
|
|
23
|
-
built JS.
|
|
24
|
-
|
|
25
|
-
### Decision: structured flags as the single source of truth
|
|
26
|
-
Replace the bare-string `KNOWN_FLAGS` array (`src/codegen/bin/cli.ts`) with a
|
|
27
|
-
structured table — one entry per flag:
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
interface FlagSpec {
|
|
31
|
-
name: string // '--service-name'
|
|
32
|
-
arg?: string // '<name>' (omitted for booleans)
|
|
33
|
-
description: string // one line
|
|
34
|
-
group: 'Source' | 'Output' | 'Codegen' | 'Targets' | 'Misc'
|
|
35
|
-
default?: string // shown in help when meaningful, e.g. 'Api'
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Derive **both** the validation set (the current `KNOWN_FLAGS` membership check)
|
|
40
|
-
and the `--help` output from this table — adding a flag now documents it and the
|
|
41
|
-
two can never drift. This directly resolves the CLAUDE.md warning that
|
|
42
|
-
`KNOWN_FLAGS` must be hand-synced with `parseArgs`.
|
|
43
|
-
|
|
44
|
-
Behaviour:
|
|
45
|
-
- `--help` / `-h` / **bare invocation with no args** → print grouped usage
|
|
46
|
-
(header, usage line, flags grouped by `group` with aligned descriptions and
|
|
47
|
-
defaults) and **exit 0**.
|
|
48
|
-
- Exclude `--help`/`-h` from the did-you-mean candidate set so the suggestion is
|
|
49
|
-
never actively misleading.
|
|
50
|
-
|
|
51
|
-
### Alternatives considered
|
|
52
|
-
- *Hand-written usage block, `KNOWN_FLAGS` stays bare strings.* Rejected:
|
|
53
|
-
descriptions live apart from the flag list and drift — exactly the duplication
|
|
54
|
-
the codebase warns against.
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## #2 — Offline doc-envelope emission (LOW)
|
|
59
|
-
|
|
60
|
-
### Problem
|
|
61
|
-
`--file <path>` codegen works, but nothing *produces* that file. The envelope
|
|
62
|
-
only exists in-process via `builder.toDocEnvelope(cfg)`, so the regen loop forces
|
|
63
|
-
either a running server (`codegen --url`) or a hand-written `JSON.stringify`
|
|
64
|
-
script.
|
|
65
|
-
|
|
66
|
-
### Decision: exported helper only
|
|
67
|
-
Ship a public, stable helper:
|
|
68
|
-
|
|
69
|
-
```ts
|
|
70
|
-
export async function writeDocEnvelope(
|
|
71
|
-
source: DocEnvelope | { toDocEnvelope(): DocEnvelope } | DocRegistry,
|
|
72
|
-
path: string,
|
|
73
|
-
): Promise<void>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
- Accepts a built `HonoAppBuilder`/`DocRegistry` (anything with
|
|
77
|
-
`toDocEnvelope()`) **or** a plain `DocEnvelope`.
|
|
78
|
-
- Serializes pretty JSON to `path` (creating parent dirs).
|
|
79
|
-
- Exported from a stable entry point and documented with a 3-line script:
|
|
80
|
-
`build builder → writeDocEnvelope(builder, 'docs.json') → codegen --file docs.json`.
|
|
81
|
-
|
|
82
|
-
No module-loading magic in the CLI — the user owns the tiny emit script, which is
|
|
83
|
-
the most portable option and avoids a TS-loader dependency.
|
|
84
|
-
|
|
85
|
-
### Alternatives considered
|
|
86
|
-
- *CLI `--builder <module>` that imports the user's builder in-process.* Rejected
|
|
87
|
-
for now: importing user TS/ESM requires a loader (tsx/jiti) the package doesn't
|
|
88
|
-
carry; pushes complexity and failure modes into the CLI.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## #3 — Shared types keyed on `$id` (MEDIUM)
|
|
93
|
-
|
|
94
|
-
### Problem
|
|
95
|
-
Codegen inlines a fresh structural literal at every route site. The same `Message`
|
|
96
|
-
entity is emitted ~4× under different names in one scope file
|
|
97
|
-
(`GetThread.Response.Message`, `ListMessages.Response.RootType`,
|
|
98
|
-
`SendMessage.Response.Body`, nested in `CreateThread`), none connected to the
|
|
99
|
-
authored `@shared/schemas` `Message`. The copies type-check only because the
|
|
100
|
-
shapes coincide today; a field change one codegen path misses drifts **silently**.
|
|
101
|
-
|
|
102
|
-
### Decision: hoist in codegen, leave the envelope untouched
|
|
103
|
-
|
|
104
|
-
**Architectural call.** The `$id` already rides along on route subschemas in the
|
|
105
|
-
envelope (verified: `Type.Object({…}, { $id, title })` survives `toDocEnvelope()`).
|
|
106
|
-
Rather than change the envelope into a `$defs`/`$ref` table — which would break
|
|
107
|
-
every already-serialized envelope and force the Kotlin/Swift pipelines to become
|
|
108
|
-
`$ref`-aware — the **TS codegen** does a pre-pass over the inlined schemas. Result:
|
|
109
|
-
zero envelope-shape change, zero impact on Kotlin/Swift, already-serialized
|
|
110
|
-
envelopes keep working.
|
|
111
|
-
|
|
112
|
-
#### Identity rule (correctness-critical)
|
|
113
|
-
- Hoist **only** schemas carrying `$id`. `$id` is the dedup key (globally unique).
|
|
114
|
-
- The generated type **name** comes from `title` (fallback: derive from `$id`).
|
|
115
|
-
- Schemas with a `title` but no `$id` are **not** hoisted — they stay inlined
|
|
116
|
-
exactly as today. Identity is *declared*, never inferred from structure.
|
|
117
|
-
- If two subschemas share an `$id` but have **divergent bodies** → **hard error**
|
|
118
|
-
at collect time (never silently pick one).
|
|
119
|
-
- Schemas without `$id` produce byte-identical output to today, so existing
|
|
120
|
-
consumers who never set `$id` see no change.
|
|
121
|
-
|
|
122
|
-
#### Components
|
|
123
|
-
1. **`src/codegen/collect-models.ts`** (new) — walks every route's JSON schema
|
|
124
|
-
recursively (all four route kinds: rpc/api/stream/http-stream slots), collects
|
|
125
|
-
each subschema carrying `$id` into a model registry
|
|
126
|
-
`{ $id, name, schema }`. Detects `$id` body-divergence collisions and throws.
|
|
127
|
-
Also detects generated-name collisions between distinct `$id`s and disambiguates
|
|
128
|
-
(`Message`, `Message2`, …) deterministically by first-seen order.
|
|
129
|
-
|
|
130
|
-
2. **`_models.ts`** (new emitted file) — the single hub:
|
|
131
|
-
- For each model whose `$id` is in the `sharedTypesImport` map → emit a
|
|
132
|
-
re-export: `export { Message } from '@shared/schemas'` (renamed to the
|
|
133
|
-
model name if `name` differs).
|
|
134
|
-
- Otherwise → emit a generated named type for the model schema (via ajsc).
|
|
135
|
-
- Scopes **always** import from `_models.ts`, regardless of generated-vs-vendored.
|
|
136
|
-
Flipping a type between the two is a one-line change here with zero scope churn.
|
|
137
|
-
|
|
138
|
-
3. **`emit-scope` change** — when a route's schema contains a hoisted `$id`
|
|
139
|
-
subschema, the emitted route type **references** the model's name (import in
|
|
140
|
-
flat mode, qualified `${Service}Models.Message` in namespace mode) instead of
|
|
141
|
-
inlining the literal. Non-hoisted subschemas emit unchanged.
|
|
142
|
-
|
|
143
|
-
4. **`sharedTypesImport` config** (config-file only — a map doesn't fit a CLI flag):
|
|
144
|
-
```jsonc
|
|
145
|
-
// ts-procedures-codegen.config.json
|
|
146
|
-
"sharedTypesImport": {
|
|
147
|
-
"https://schemas.example/message": { "module": "@shared/schemas", "name": "Message" }
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
5. **Flag:** `--share-models` defaults **on** (consistent with the other
|
|
152
|
-
good-by-default flags); `--no-share-models` opts out and restores pure
|
|
153
|
-
per-route inlining. Added to the structured flag table from #1.
|
|
154
|
-
|
|
155
|
-
#### Scope
|
|
156
|
-
- **TS target only.** Kotlin/Swift continue to inline (they simply ignore `$id`
|
|
157
|
-
metadata; the envelope is unchanged so their pipelines are untouched).
|
|
158
|
-
|
|
159
|
-
#### Key technical risk — verify first
|
|
160
|
-
The plan opens with a short **verification spike** before committing the emission
|
|
161
|
-
mechanism:
|
|
162
|
-
- **ajsc `$ref` behaviour (v7.2.0):** confirm how ajsc emits a `$ref` to a hoisted
|
|
163
|
-
model — whether it produces a *reference* to a named type or inlines it. Per
|
|
164
|
-
CLAUDE.md ajsc "resolves `$defs`/`$ref`", which may mean *inline*. Two paths:
|
|
165
|
-
- *Preferred:* feed ajsc a route schema with the hoisted subschema replaced by
|
|
166
|
-
`{ $ref: '#/$defs/Message' }` + a `$defs` table, and capture ajsc's named-type
|
|
167
|
-
reference.
|
|
168
|
-
- *Fallback:* emit model types standalone, then post-emit string-substitute the
|
|
169
|
-
inlined literal with the model name — the same word-boundary patching approach
|
|
170
|
-
`renameExtractedTypes` (`src/codegen/emit-types.ts`) already uses.
|
|
171
|
-
- **Nested `$id` survival:** confirm `extractJsonSchema` preserves `$id` on
|
|
172
|
-
**nested** subschemas (root `$id` is verified; nested is not). If stripped, fix
|
|
173
|
-
extraction to preserve it — this is a prerequisite for hoisting nested entities
|
|
174
|
-
like `Thread.messages: Message[]`.
|
|
175
|
-
|
|
176
|
-
### Alternatives considered
|
|
177
|
-
- *Change the envelope to `$defs`/`$ref`.* Rejected: breaks serialized envelopes
|
|
178
|
-
and forces all targets to become `$ref`-aware.
|
|
179
|
-
- *Hoist into existing `_types.ts`.* Rejected: mixes domain entities with
|
|
180
|
-
transport/runtime types and only works in self-contained mode.
|
|
181
|
-
- *Per-scope first-declarer-owns.* Rejected: implicit cross-scope ordering/coupling.
|
|
182
|
-
- *Auto-merge structurally-identical types.* Explicitly rejected per the feedback:
|
|
183
|
-
coincidental shape-equality must not couple two scopes.
|
|
184
|
-
- *Dedup by `$id` OR `title`.* Rejected: two unrelated schemas titled `Response`
|
|
185
|
-
would wrongly merge — weak identity.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## #4 — Dynamic auth seam: function-valued headers (MEDIUM)
|
|
190
|
-
|
|
191
|
-
### Problem
|
|
192
|
-
`config.headers` is a static `Record<string,string>` (`src/client/types.ts:202`),
|
|
193
|
-
captured once at construction. The only dynamic seam is `onBeforeRequest`, which
|
|
194
|
-
is discoverable only by reading `_client.ts`. A live bearer token set in `headers`
|
|
195
|
-
silently goes stale after the next login (the easy-and-wrong path compiles).
|
|
196
|
-
|
|
197
|
-
### Decision: function-valued headers + signposting docs
|
|
198
|
-
Make `headers` accept a function on both the client `defaults` and per-call options:
|
|
199
|
-
|
|
200
|
-
```ts
|
|
201
|
-
type HeadersInit =
|
|
202
|
-
| Record<string, string>
|
|
203
|
-
| (() => Record<string, string> | Promise<Record<string, string>>)
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
- `ProcedureCallDefaults.headers` and `ProcedureCallOptions.headers` adopt
|
|
207
|
-
`HeadersInit` (`src/client/types.ts`).
|
|
208
|
-
- Resolution happens on the **async request path**: `call.ts` / `stream.ts` await
|
|
209
|
-
header resolution before building the `AdapterRequest`. The static-record path
|
|
210
|
-
stays synchronous and fast (no behavioural change when headers is a plain object).
|
|
211
|
-
- Merge semantics preserved: resolve defaults-headers and per-call-headers to
|
|
212
|
-
records, then merge with per-call winning — `onBeforeRequest` still has final say.
|
|
213
|
-
- The resolved `AdapterRequest.headers` remains a plain `Record<string,string>` —
|
|
214
|
-
adapters are unaffected.
|
|
215
|
-
- Regenerates into self-contained `_types.ts` / `_client.ts`; direct consumers get
|
|
216
|
-
it from `ts-procedures/client`.
|
|
217
|
-
|
|
218
|
-
**Docs:** add a short auth section (in the generated-client docs /
|
|
219
|
-
`client-and-codegen.md`) presenting function-valued `headers` and `onBeforeRequest`
|
|
220
|
-
as the canonical auth seams, with an explicit "a token in static `headers` goes
|
|
221
|
-
stale — use a function or the hook" warning.
|
|
222
|
-
|
|
223
|
-
### Alternatives considered
|
|
224
|
-
- *First-class `auth`/`bearer` option wiring the hook internally.* Not chosen this
|
|
225
|
-
round — function-valued headers is the general-purpose primitive that also covers
|
|
226
|
-
auth; a dedicated `auth` sugar can layer on later if wanted.
|
|
227
|
-
- *Docs only.* Rejected: the easy-and-wrong static-headers path still compiles.
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## #5 — Scaffolder file-naming knobs (LOW)
|
|
232
|
-
|
|
233
|
-
### Problem
|
|
234
|
-
The scaffolder (the AI skill `agent_config/claude-code/skills/ts-procedures/SKILL.md`
|
|
235
|
-
scaffold mode + `templates/*.md`, **not** a CLI) hardcodes `{{Name}}.procedure.ts` —
|
|
236
|
-
PascalCase, one file per procedure — so every scaffold is renamed and folded into a
|
|
237
|
-
per-scope file by hand.
|
|
238
|
-
|
|
239
|
-
### Decision: `fileNameStyle` + `groupBy` with auto-default
|
|
240
|
-
Add two scaffold-mode args, surfaced in `SKILL.md` and consumed by the templates:
|
|
241
|
-
- `fileNameStyle`: `'PascalCase'` (current) | `'kebab.concern'` (e.g.
|
|
242
|
-
`get-user.procedure.ts`).
|
|
243
|
-
- `groupBy`: `'flat'` (current, CWD) | `'scope'` (e.g. `<scope>/get-user.procedure.ts`,
|
|
244
|
-
scope taken from the procedure config).
|
|
245
|
-
|
|
246
|
-
When **unspecified**, the skill infers the default by inspecting the target
|
|
247
|
-
directory's existing convention (per-scope kebab files vs flat Pascal) — so a
|
|
248
|
-
scaffold matches the consumer's layout with no manual rename pass.
|
|
249
|
-
|
|
250
|
-
Changes:
|
|
251
|
-
- `SKILL.md` scaffold-mode section: document the two args + the auto-default
|
|
252
|
-
inference instruction; update the "Files Generated" table to show the computed
|
|
253
|
-
filename.
|
|
254
|
-
- `templates/*.md`: replace the hardcoded `{{Name}}.<concern>.ts` output with a
|
|
255
|
-
computed `{{fileName}}` placeholder and an optional `{{scope}}/` directory
|
|
256
|
-
prefix, plus the placeholder-derivation notes (`{{kebab}}` already exists).
|
|
257
|
-
|
|
258
|
-
### Alternatives considered
|
|
259
|
-
- *Add knobs, explicit-only (no auto-detect).* Rejected: misses the zero-config
|
|
260
|
-
match to the repo layout, which is the actual friction.
|
|
261
|
-
- *Defer.* Rejected by scope decision (all three findings actioned this round).
|
|
262
|
-
|
|
263
|
-
---
|
|
264
|
-
|
|
265
|
-
## Testing strategy
|
|
266
|
-
- **#1:** unit test `--help`/`-h`/bare → exit 0 + usage contains every flag from the
|
|
267
|
-
table; `--help` not in did-you-mean candidates; unknown flag still errors.
|
|
268
|
-
- **#2:** unit test `writeDocEnvelope` with (a) a plain envelope, (b) a builder-like
|
|
269
|
-
object — round-trips to disk and re-loads via `resolveEnvelope --file`.
|
|
270
|
-
- **#3:** spike verification first; then fixture-based tests on the canonical
|
|
271
|
-
`__fixtures__/users-envelope.json` extended with `$id`'d schemas — assert one
|
|
272
|
-
`_models.ts` declaration per `$id`, route types reference it, `$id` collision
|
|
273
|
-
throws, `sharedTypesImport` produces re-exports, and `--no-share-models` restores
|
|
274
|
-
byte-identical legacy output. Add a no-`$id` regression asserting unchanged output.
|
|
275
|
-
- **#4:** unit test function-valued `headers` (sync + async) resolves per request,
|
|
276
|
-
merges with per-call, and `onBeforeRequest` still overrides; static-record path
|
|
277
|
-
unchanged.
|
|
278
|
-
- **#5:** verify by scaffolding under each `fileNameStyle`/`groupBy` combination and
|
|
279
|
-
the auto-default inference (skill-level / template review).
|
|
280
|
-
|
|
281
|
-
## Out of scope (follow-up)
|
|
282
|
-
- `ts-channels` codegen shared-types fix (identical to #3).
|
|
283
|
-
- Cross-scaffolder alignment with `ts-channels #9` / `mvc-kit #2` (#5).
|
|
284
|
-
- A first-class `auth`/`bearer` client option (sugar over #4).
|
|
285
|
-
- Kotlin/Swift shared-model emission.
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { ProcedureError, ProcedureRegistrationError, ProcedureValidationError, ProcedureYieldValidationError } from './errors.js'
|
|
2
|
-
import { computeSchema } from './schema/compute-schema.js'
|
|
3
|
-
import type { Prettify, TSchemaLib } from './schema/types.js'
|
|
4
|
-
import { captureDefinitionInfo } from './stack-utils.js'
|
|
5
|
-
import type {
|
|
6
|
-
HttpMethod, TBuilderConfig, TStreamContext,
|
|
7
|
-
THttpStreamProcedureRegistration, TProcedureRegistration, TStreamProcedureRegistration, THttpProcedureRegistration,
|
|
8
|
-
} from './types.js'
|
|
9
|
-
import { checkPathParamConsistency } from './create-http.js'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Wraps a user's AsyncIterator in a generator that:
|
|
13
|
-
* - optionally validates each yielded value
|
|
14
|
-
* - augments error stack traces with the procedure definition site
|
|
15
|
-
* - aborts the stream controller on completion
|
|
16
|
-
*/
|
|
17
|
-
async function* yieldingWrapper(
|
|
18
|
-
userIterator: AsyncIterator<any, any, unknown>,
|
|
19
|
-
validateYields: boolean,
|
|
20
|
-
validations: { yield?: (value: any) => { errors?: any[] } },
|
|
21
|
-
abortController: AbortController,
|
|
22
|
-
definitionInfo: ReturnType<typeof captureDefinitionInfo>,
|
|
23
|
-
name: string,
|
|
24
|
-
): AsyncGenerator<any, any, unknown> {
|
|
25
|
-
try {
|
|
26
|
-
let result = await userIterator.next()
|
|
27
|
-
while (!result.done) {
|
|
28
|
-
const value = result.value
|
|
29
|
-
if (validateYields && validations.yield) {
|
|
30
|
-
const { errors } = validations.yield(value)
|
|
31
|
-
if (errors) throw new ProcedureYieldValidationError(name, `Yield validation error for ${name}`, errors, definitionInfo)
|
|
32
|
-
}
|
|
33
|
-
yield value
|
|
34
|
-
result = await userIterator.next()
|
|
35
|
-
}
|
|
36
|
-
return result.value
|
|
37
|
-
} catch (error: any) {
|
|
38
|
-
if (definitionInfo.definedAt && error && typeof error.stack === 'string') {
|
|
39
|
-
const { file, line, column } = definitionInfo.definedAt
|
|
40
|
-
error.stack = `${error.stack}\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
41
|
-
}
|
|
42
|
-
throw error
|
|
43
|
-
} finally {
|
|
44
|
-
try { await userIterator.return?.(undefined) } catch { /* swallow */ }
|
|
45
|
-
abortController.abort('stream-completed')
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function makeCreateHttpStream<TContext>(
|
|
50
|
-
procedures: Map<string, TProcedureRegistration<TContext, any> | TStreamProcedureRegistration<TContext, any> | THttpProcedureRegistration<TContext> | THttpStreamProcedureRegistration<TContext>>,
|
|
51
|
-
builder?: { config?: TBuilderConfig; onCreate?: (procedure: any) => void },
|
|
52
|
-
) {
|
|
53
|
-
return function CreateHttpStream<
|
|
54
|
-
TName extends string,
|
|
55
|
-
TReq extends Record<string, unknown> | undefined,
|
|
56
|
-
TYieldType,
|
|
57
|
-
TReturnType = void,
|
|
58
|
-
TResHeaders = undefined,
|
|
59
|
-
TErrorKey extends string = string,
|
|
60
|
-
>(
|
|
61
|
-
name: TName,
|
|
62
|
-
config: {
|
|
63
|
-
path: string
|
|
64
|
-
method: HttpMethod
|
|
65
|
-
scope?: string
|
|
66
|
-
errors?: TErrorKey[]
|
|
67
|
-
description?: string
|
|
68
|
-
schema: {
|
|
69
|
-
req?: TReq
|
|
70
|
-
yield?: TYieldType
|
|
71
|
-
returnType?: TReturnType
|
|
72
|
-
res?: TResHeaders extends undefined ? undefined : { headers: TResHeaders }
|
|
73
|
-
}
|
|
74
|
-
validateYields?: boolean
|
|
75
|
-
},
|
|
76
|
-
handler: TResHeaders extends undefined
|
|
77
|
-
? (
|
|
78
|
-
ctx: Prettify<TContext & TStreamContext>,
|
|
79
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
80
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
81
|
-
: (
|
|
82
|
-
ctx: Prettify<TContext & TStreamContext>,
|
|
83
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
84
|
-
) => Promise<{ headers: TSchemaLib<TResHeaders>; stream: AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown> }>,
|
|
85
|
-
) {
|
|
86
|
-
const definitionInfo = captureDefinitionInfo()
|
|
87
|
-
|
|
88
|
-
if (procedures.has(name)) throw new Error(`Procedure with name ${name} is already registered`)
|
|
89
|
-
if ((config.schema as any)?.params) {
|
|
90
|
-
throw new ProcedureRegistrationError(name, `Use schema.req.body (or schema.req.query) instead of schema.params on CreateHttpStream. Procedure: "${name}".`, definitionInfo)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const { jsonSchema, validations } = computeSchema(name, {
|
|
94
|
-
req: config.schema.req as Record<string, unknown> | undefined,
|
|
95
|
-
yieldType: config.schema.yield,
|
|
96
|
-
returnType: config.schema.returnType,
|
|
97
|
-
}, definitionInfo)
|
|
98
|
-
|
|
99
|
-
const pathParamsSchema = (jsonSchema.req as Record<string, unknown> | undefined)?.pathParams
|
|
100
|
-
checkPathParamConsistency(name, config.path, pathParamsSchema as Record<string, unknown> | undefined, definitionInfo)
|
|
101
|
-
|
|
102
|
-
const errorFactory = (message: string, meta?: object) => new ProcedureError(name, message, meta, definitionInfo)
|
|
103
|
-
const validateYields = config.validateYields ?? false
|
|
104
|
-
|
|
105
|
-
const wrappedHandler = async (ctx: TContext, req: any): Promise<{ stream: AsyncGenerator<any, any, unknown>; initialHeaders?: Record<string, string> }> => {
|
|
106
|
-
const abortController = new AbortController()
|
|
107
|
-
const skipValidation = (ctx as { isPrevalidated?: boolean }).isPrevalidated || builder?.config?.noRuntimeValidation
|
|
108
|
-
|
|
109
|
-
if (validations?.req && !skipValidation) {
|
|
110
|
-
for (const [channel, validator] of Object.entries(validations.req)) {
|
|
111
|
-
const channelValue = (req as Record<string, unknown>)?.[channel]
|
|
112
|
-
const { errors } = validator(channelValue)
|
|
113
|
-
if (errors) {
|
|
114
|
-
throw new ProcedureValidationError(name, `Validation error for ${name} in req.${channel}`, errors, definitionInfo)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const incomingSignal = (ctx as { signal?: AbortSignal }).signal
|
|
120
|
-
const signal = incomingSignal ? AbortSignal.any([incomingSignal, abortController.signal]) : abortController.signal
|
|
121
|
-
const streamCtx: TStreamContext = { error: errorFactory, signal }
|
|
122
|
-
|
|
123
|
-
const handlerResult: any = (handler as any)({ ...ctx, ...streamCtx }, req)
|
|
124
|
-
|
|
125
|
-
if (typeof handlerResult?.[Symbol.asyncIterator] === 'function') {
|
|
126
|
-
// Plain async generator from user
|
|
127
|
-
const userIterator = (handlerResult as AsyncGenerator<any, any, unknown>)[Symbol.asyncIterator]()
|
|
128
|
-
return { stream: yieldingWrapper(userIterator, validateYields, validations, abortController, definitionInfo, name) }
|
|
129
|
-
} else if (typeof handlerResult?.then === 'function') {
|
|
130
|
-
// Async preamble: Promise<{ headers, stream }>
|
|
131
|
-
const resolved = await handlerResult
|
|
132
|
-
if (!resolved || typeof resolved !== 'object' || !('stream' in resolved)) {
|
|
133
|
-
throw new ProcedureError(name, `CreateHttpStream handler returned a Promise that did not resolve to { headers, stream }. Got: ${JSON.stringify(resolved)}`, undefined, definitionInfo)
|
|
134
|
-
}
|
|
135
|
-
const userIterator = (resolved.stream as AsyncGenerator<any, any, unknown>)[Symbol.asyncIterator]()
|
|
136
|
-
return {
|
|
137
|
-
stream: yieldingWrapper(userIterator, validateYields, validations, abortController, definitionInfo, name),
|
|
138
|
-
initialHeaders: resolved.headers,
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
throw new ProcedureError(name, `CreateHttpStream handler must return an AsyncGenerator or Promise<{ headers, stream }>.`, undefined, definitionInfo)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const registeredProcedure: THttpStreamProcedureRegistration<TContext> = {
|
|
146
|
-
name,
|
|
147
|
-
kind: 'http-stream',
|
|
148
|
-
config: {
|
|
149
|
-
path: config.path,
|
|
150
|
-
method: config.method,
|
|
151
|
-
scope: config.scope,
|
|
152
|
-
errors: config.errors as string[] | undefined,
|
|
153
|
-
description: config.description,
|
|
154
|
-
schema: jsonSchema as any,
|
|
155
|
-
validation: { req: validations.req, yield: validations.yield },
|
|
156
|
-
validateYields,
|
|
157
|
-
},
|
|
158
|
-
handler: wrappedHandler as any,
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
procedures.set(name, registeredProcedure as any)
|
|
162
|
-
builder?.onCreate?.(registeredProcedure)
|
|
163
|
-
|
|
164
|
-
const info = { name, kind: 'http-stream' as const, ...registeredProcedure.config }
|
|
165
|
-
|
|
166
|
-
// Explicit return type — preserves TReq/TYieldType/TReturnType through the public callable.
|
|
167
|
-
// Do NOT use `typeof registeredProcedure.handler` — that erases generics to `any`.
|
|
168
|
-
return {
|
|
169
|
-
[name]: registeredProcedure.handler,
|
|
170
|
-
procedure: registeredProcedure.handler,
|
|
171
|
-
info,
|
|
172
|
-
} as {
|
|
173
|
-
[K in TName]: (
|
|
174
|
-
ctx: Prettify<TContext>,
|
|
175
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
176
|
-
) => Promise<{
|
|
177
|
-
stream: AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
178
|
-
initialHeaders?: Record<string, string>
|
|
179
|
-
}>
|
|
180
|
-
} & {
|
|
181
|
-
procedure: (
|
|
182
|
-
ctx: Prettify<TContext>,
|
|
183
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
184
|
-
) => Promise<{
|
|
185
|
-
stream: AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
186
|
-
initialHeaders?: Record<string, string>
|
|
187
|
-
}>
|
|
188
|
-
info: typeof info
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
package/src/create-http.ts
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { ProcedureError, ProcedureRegistrationError, ProcedureValidationError } from './errors.js'
|
|
2
|
-
import { computeSchema } from './schema/compute-schema.js'
|
|
3
|
-
import type { Prettify, TSchemaLib } from './schema/types.js'
|
|
4
|
-
import { captureDefinitionInfo } from './stack-utils.js'
|
|
5
|
-
import type {
|
|
6
|
-
TBuilderConfig,
|
|
7
|
-
TCreateHttpConfig,
|
|
8
|
-
TLocalContext,
|
|
9
|
-
THttpProcedureRegistration,
|
|
10
|
-
THttpStreamProcedureRegistration,
|
|
11
|
-
TProcedureRegistration,
|
|
12
|
-
TStreamProcedureRegistration,
|
|
13
|
-
} from './types.js'
|
|
14
|
-
|
|
15
|
-
type Infer<T> = TSchemaLib<T>
|
|
16
|
-
|
|
17
|
-
export type HttpReturn<TRes> =
|
|
18
|
-
TRes extends { body: infer B; headers: infer H } ? { body: Infer<B>; headers: Infer<H> }
|
|
19
|
-
: TRes extends { headers: infer H } ? { headers: Infer<H> }
|
|
20
|
-
: TRes extends { body: infer B } ? Infer<B>
|
|
21
|
-
: void
|
|
22
|
-
|
|
23
|
-
const PATH_PARAM_RE = /:([a-zA-Z_][a-zA-Z0-9_]*)/g
|
|
24
|
-
|
|
25
|
-
function extractPathParamNames(path: string): string[] {
|
|
26
|
-
const matches = path.match(PATH_PARAM_RE)
|
|
27
|
-
return matches ? matches.map((m) => m.slice(1)) : []
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function checkPathParamConsistency(
|
|
31
|
-
procedureName: string,
|
|
32
|
-
path: string,
|
|
33
|
-
pathParamsSchema: Record<string, unknown> | undefined,
|
|
34
|
-
definitionInfo: ReturnType<typeof captureDefinitionInfo>,
|
|
35
|
-
) {
|
|
36
|
-
const pathParamNames = extractPathParamNames(path)
|
|
37
|
-
const hasPathParams = pathParamNames.length > 0
|
|
38
|
-
const hasSchema = pathParamsSchema !== undefined
|
|
39
|
-
|
|
40
|
-
if (hasPathParams && !hasSchema) {
|
|
41
|
-
throw new ProcedureRegistrationError(
|
|
42
|
-
procedureName,
|
|
43
|
-
`Path "${path}" has path parameters [${pathParamNames.join(', ')}] but schema.req.pathParams is not defined.`,
|
|
44
|
-
definitionInfo,
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
if (!hasPathParams && hasSchema) {
|
|
48
|
-
throw new ProcedureRegistrationError(
|
|
49
|
-
procedureName,
|
|
50
|
-
`schema.req.pathParams is defined but path "${path}" has no path parameters.`,
|
|
51
|
-
definitionInfo,
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
if (hasPathParams && hasSchema) {
|
|
55
|
-
const schemaProperties = (pathParamsSchema as { properties?: Record<string, unknown> }).properties
|
|
56
|
-
if (schemaProperties) {
|
|
57
|
-
const schemaKeys = Object.keys(schemaProperties)
|
|
58
|
-
const missing = pathParamNames.filter((p) => !schemaKeys.includes(p))
|
|
59
|
-
const extra = schemaKeys.filter((k) => !pathParamNames.includes(k))
|
|
60
|
-
if (missing.length > 0 || extra.length > 0) {
|
|
61
|
-
const parts: string[] = []
|
|
62
|
-
if (missing.length > 0) parts.push(`path has [${missing.join(', ')}] missing from schema`)
|
|
63
|
-
if (extra.length > 0) parts.push(`schema has [${extra.join(', ')}] not in path`)
|
|
64
|
-
throw new ProcedureRegistrationError(
|
|
65
|
-
procedureName,
|
|
66
|
-
`Path param mismatch for "${procedureName}": ${parts.join('; ')}.`,
|
|
67
|
-
definitionInfo,
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function makeCreateHttp<TContext>(
|
|
75
|
-
procedures: Map<
|
|
76
|
-
string,
|
|
77
|
-
| TProcedureRegistration<TContext, any>
|
|
78
|
-
| TStreamProcedureRegistration<TContext, any>
|
|
79
|
-
| THttpProcedureRegistration<TContext>
|
|
80
|
-
| THttpStreamProcedureRegistration<TContext>
|
|
81
|
-
>,
|
|
82
|
-
builder?: { config?: TBuilderConfig; onCreate?: (procedure: any) => void },
|
|
83
|
-
) {
|
|
84
|
-
return function CreateHttp<
|
|
85
|
-
TName extends string,
|
|
86
|
-
TReq extends Record<string, unknown> | undefined,
|
|
87
|
-
TRes extends { body?: unknown; headers?: unknown } | undefined = undefined,
|
|
88
|
-
TErrorKey extends string = string,
|
|
89
|
-
>(
|
|
90
|
-
name: TName,
|
|
91
|
-
config: TCreateHttpConfig<TReq, TRes, TErrorKey>,
|
|
92
|
-
handler: (
|
|
93
|
-
ctx: Prettify<TContext & TLocalContext>,
|
|
94
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
95
|
-
) => Promise<HttpReturn<TRes>>,
|
|
96
|
-
) {
|
|
97
|
-
const definitionInfo = captureDefinitionInfo()
|
|
98
|
-
|
|
99
|
-
if (procedures.has(name)) {
|
|
100
|
-
throw new Error(`Procedure with name ${name} is already registered`)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if ((config.schema as any)?.params) {
|
|
104
|
-
throw new ProcedureRegistrationError(
|
|
105
|
-
name,
|
|
106
|
-
`Use schema.req.body (or schema.req.query) instead of schema.params on CreateHttp. Procedure: "${name}".`,
|
|
107
|
-
definitionInfo,
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const { jsonSchema, validations } = computeSchema(
|
|
112
|
-
name,
|
|
113
|
-
{
|
|
114
|
-
req: config.schema.req as Record<string, unknown> | undefined,
|
|
115
|
-
res: config.schema.res as { body?: unknown; headers?: unknown } | undefined,
|
|
116
|
-
},
|
|
117
|
-
definitionInfo,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
const pathParamsSchema = (jsonSchema.req as Record<string, unknown> | undefined)?.pathParams
|
|
121
|
-
checkPathParamConsistency(
|
|
122
|
-
name,
|
|
123
|
-
config.path,
|
|
124
|
-
pathParamsSchema as Record<string, unknown> | undefined,
|
|
125
|
-
definitionInfo,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
const errorFactory = (message: string, meta?: object) =>
|
|
129
|
-
new ProcedureError(name, message, meta, definitionInfo)
|
|
130
|
-
|
|
131
|
-
const registeredProcedure: THttpProcedureRegistration<TContext> = {
|
|
132
|
-
name,
|
|
133
|
-
kind: 'http',
|
|
134
|
-
config: {
|
|
135
|
-
path: config.path,
|
|
136
|
-
method: config.method,
|
|
137
|
-
successStatus: config.successStatus,
|
|
138
|
-
scope: config.scope,
|
|
139
|
-
errors: config.errors as string[] | undefined,
|
|
140
|
-
description: config.description,
|
|
141
|
-
schema: jsonSchema as any,
|
|
142
|
-
validation: { req: validations.req },
|
|
143
|
-
},
|
|
144
|
-
handler: async (ctx: TContext, req: any) => {
|
|
145
|
-
try {
|
|
146
|
-
const skipValidation =
|
|
147
|
-
(ctx as { isPrevalidated?: boolean }).isPrevalidated ||
|
|
148
|
-
builder?.config?.noRuntimeValidation
|
|
149
|
-
|
|
150
|
-
if (validations?.req && !skipValidation) {
|
|
151
|
-
for (const [channel, validator] of Object.entries(validations.req)) {
|
|
152
|
-
const channelValue = (req as Record<string, unknown>)?.[channel]
|
|
153
|
-
const { errors } = validator(channelValue)
|
|
154
|
-
if (errors) {
|
|
155
|
-
throw new ProcedureValidationError(
|
|
156
|
-
name,
|
|
157
|
-
`Validation error for ${name} in req.${channel}`,
|
|
158
|
-
errors,
|
|
159
|
-
definitionInfo,
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const localCtx: TLocalContext = { error: errorFactory }
|
|
166
|
-
return await handler({ ...ctx, ...localCtx } as any, req)
|
|
167
|
-
} catch (error: any) {
|
|
168
|
-
if (error instanceof ProcedureError) throw error
|
|
169
|
-
const err = new ProcedureError(
|
|
170
|
-
name,
|
|
171
|
-
`Error in handler for ${name} - ${error?.message}`,
|
|
172
|
-
undefined,
|
|
173
|
-
definitionInfo,
|
|
174
|
-
)
|
|
175
|
-
err.cause = error
|
|
176
|
-
if (error.stack && definitionInfo.definedAt) {
|
|
177
|
-
const { file, line, column } = definitionInfo.definedAt
|
|
178
|
-
err.stack =
|
|
179
|
-
error.stack + `\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
180
|
-
} else if (error.stack) {
|
|
181
|
-
err.stack = error.stack
|
|
182
|
-
}
|
|
183
|
-
throw err
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
procedures.set(name, registeredProcedure as any)
|
|
189
|
-
builder?.onCreate?.(registeredProcedure)
|
|
190
|
-
|
|
191
|
-
const info = { name, ...registeredProcedure.config }
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
[name]: registeredProcedure.handler,
|
|
195
|
-
procedure: registeredProcedure.handler,
|
|
196
|
-
info,
|
|
197
|
-
} as {
|
|
198
|
-
[K in TName]: (
|
|
199
|
-
ctx: Prettify<TContext>,
|
|
200
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
201
|
-
) => Promise<HttpReturn<TRes>>
|
|
202
|
-
} & {
|
|
203
|
-
procedure: (
|
|
204
|
-
ctx: Prettify<TContext>,
|
|
205
|
-
req: TReq extends Record<string, unknown> ? Prettify<{ [K in keyof TReq]: TSchemaLib<TReq[K]> }> : undefined,
|
|
206
|
-
) => Promise<HttpReturn<TRes>>
|
|
207
|
-
info: typeof info
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|