ts-procedures 8.6.0 → 9.1.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/CHANGELOG.md +540 -0
- 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 +37 -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-scope.d.ts +13 -30
- package/build/codegen/emit-scope.js +15 -844
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +67 -0
- 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} +27 -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 +183 -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 +10 -4
- package/docs/client-error-handling.md +5 -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 +58 -6
- package/docs/migration-v8-to-v9.md +200 -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 +25 -48
- 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/__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 +104 -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-scope.test.ts +75 -2
- package/src/codegen/emit-scope.ts +15 -1048
- 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/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/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} +31 -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 +196 -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/2026-06-08-dx-round2-declines.md +0 -45
- 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/plans/2026-06-08-codegen-dx-surfacing.md +0 -428
- 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/docs/superpowers/specs/2026-06-08-dx-feedback-round-2-design.md +0 -376
- 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,1049 +1,16 @@
|
|
|
1
|
-
import type { ScopeGroup } from './group-routes.js'
|
|
2
|
-
import type {
|
|
3
|
-
RPCHttpRouteDoc,
|
|
4
|
-
APIHttpRouteDoc,
|
|
5
|
-
StreamHttpRouteDoc,
|
|
6
|
-
HttpStreamRouteDoc,
|
|
7
|
-
} from '../implementations/types.js'
|
|
8
|
-
import {
|
|
9
|
-
jsonSchemaToTypeString,
|
|
10
|
-
jsonSchemaToTypeBody,
|
|
11
|
-
jsonSchemaToTypeBodyWithRefs,
|
|
12
|
-
jsonSchemaToExtractedTypes,
|
|
13
|
-
renameExtractedTypes,
|
|
14
|
-
extractedDeclName,
|
|
15
|
-
type AjscOptions,
|
|
16
|
-
type ExtractedTypeOutput,
|
|
17
|
-
} from './emit-types.js'
|
|
18
|
-
import { substituteModelRefs } from './model-refs.js'
|
|
19
|
-
import { CODEGEN_HEADER } from './constants.js'
|
|
20
|
-
import { toPascalCase } from './naming.js'
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Types
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
export interface EmitScopeOptions {
|
|
27
|
-
ajsc?: AjscOptions
|
|
28
|
-
clientImportPath?: string
|
|
29
|
-
namespaceTypes?: boolean
|
|
30
|
-
/** Service identifier used to namespace generated error types (defaults to 'Api'). */
|
|
31
|
-
serviceName?: string
|
|
32
|
-
/**
|
|
33
|
-
* Error keys present in the generated `_errors.ts`. Routes may list keys
|
|
34
|
-
* that aren't emitted (e.g. no schema); those are filtered out at emit time
|
|
35
|
-
* so generated code never references undefined types.
|
|
36
|
-
*/
|
|
37
|
-
errorKeys?: Set<string>
|
|
38
|
-
/**
|
|
39
|
-
* Maps a model `$id` to its shared model type name (built by the run module
|
|
40
|
-
* from ALL models — generated and imported). When present and non-empty,
|
|
41
|
-
* `$id` subschemas are rewritten to `x-named-type` nodes before ajsc; ajsc
|
|
42
|
-
* emits bare references and reports them via `referencedNamedTypes`, which
|
|
43
|
-
* drives the `import type { … } from './_models'` line. Absent/empty →
|
|
44
|
-
* identical inlining behaviour (the conversion wrappers short-circuit so
|
|
45
|
-
* output is byte-identical for envelopes without models).
|
|
46
|
-
*/
|
|
47
|
-
idToModelName?: Map<string, string>
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface RouteChunks {
|
|
51
|
-
typeDeclarations: string[]
|
|
52
|
-
callable: string
|
|
53
|
-
hasStream: boolean
|
|
54
|
-
/** True when this route emitted an `Errors` type (drives the `_errors` import at the top of the scope file). */
|
|
55
|
-
hasErrors: boolean
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface EmitRouteContext {
|
|
59
|
-
ajsc?: AjscOptions
|
|
60
|
-
namespaceTypes: boolean
|
|
61
|
-
scopePascal: string
|
|
62
|
-
serviceName: string
|
|
63
|
-
errorKeys?: Set<string>
|
|
64
|
-
/** `$id` → shared model type name; drives the substitute wrappers. */
|
|
65
|
-
idToModelName?: Map<string, string>
|
|
66
|
-
/**
|
|
67
|
-
* Accumulates the model names ajsc reported as `referencedNamedTypes` across
|
|
68
|
-
* every route in the scope. Drives the `import type { … } from './_models'`
|
|
69
|
-
* line assembled in `emitScopeFile`.
|
|
70
|
-
*/
|
|
71
|
-
referencedModels: Set<string>
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
// Helpers
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
|
|
78
1
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
function isSseEnvelope(schema: Record<string, unknown>): boolean {
|
|
95
|
-
const props = schema.properties
|
|
96
|
-
if (props == null || typeof props !== 'object') return false
|
|
97
|
-
const keys = Object.keys(props as Record<string, unknown>)
|
|
98
|
-
return keys.includes('data') && keys.includes('event') && keys.includes('id')
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Unwraps an SSE envelope schema to return the inner `data` property schema.
|
|
103
|
-
* If not an SSE envelope, returns the schema unchanged.
|
|
104
|
-
*/
|
|
105
|
-
function unwrapSseEnvelope(
|
|
106
|
-
schema: Record<string, unknown>
|
|
107
|
-
): Record<string, unknown> {
|
|
108
|
-
if (!isSseEnvelope(schema)) return schema
|
|
109
|
-
const props = schema.properties as Record<string, Record<string, unknown>>
|
|
110
|
-
const dataSchema = props['data']
|
|
111
|
-
return dataSchema ?? schema
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Returns the PascalCase display name for a route, appending `V{version}`
|
|
116
|
-
* when version > 1. Version 1 produces no suffix for backward compatibility.
|
|
117
|
-
*/
|
|
118
|
-
function versionedPascal(name: string, version: number | undefined): string {
|
|
119
|
-
const pascal = toPascalCase(name)
|
|
120
|
-
if (version != null && version > 1) return `${pascal}V${version}`
|
|
121
|
-
return pascal
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
// Shared type formatting helpers
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
|
|
128
|
-
/** Indent each line of a multi-line string by a given prefix. */
|
|
129
|
-
function indent(text: string, prefix: string): string {
|
|
130
|
-
return text.split('\n').map((line) => (line ? prefix + line : line)).join('\n')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
// Shared-model conversion wrappers (ajsc x-named-type)
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Drop-in replacement for `jsonSchemaToExtractedTypes` that, when shared models
|
|
139
|
-
* are in play, rewrites every `$id` subschema into an `x-named-type` node before
|
|
140
|
-
* ajsc. ajsc emits each as a bare verbatim reference and reports it via
|
|
141
|
-
* `referencedNamedTypes`; this wrapper folds those names into
|
|
142
|
-
* `ctx.referencedModels` so `emitScopeFile` can build the `_models` import.
|
|
143
|
-
*
|
|
144
|
-
* Short-circuits to the exact original call when no models are configured, so
|
|
145
|
-
* output is byte-identical for envelopes without `$id` models.
|
|
146
|
-
*/
|
|
147
|
-
async function convertExtracted(
|
|
148
|
-
schema: Record<string, unknown>,
|
|
149
|
-
ctx: EmitRouteContext,
|
|
150
|
-
): Promise<ExtractedTypeOutput | undefined> {
|
|
151
|
-
if (ctx.idToModelName == null || ctx.idToModelName.size === 0) {
|
|
152
|
-
return jsonSchemaToExtractedTypes(schema, ctx.ajsc)
|
|
153
|
-
}
|
|
154
|
-
const { schema: sub } = substituteModelRefs(schema, ctx.idToModelName)
|
|
155
|
-
const result = await jsonSchemaToExtractedTypes(sub, ctx.ajsc)
|
|
156
|
-
if (result != null) {
|
|
157
|
-
assertNoModelNameCollision(result)
|
|
158
|
-
for (const name of result.referencedNamedTypes) ctx.referencedModels.add(name)
|
|
159
|
-
}
|
|
160
|
-
return result
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Guards ajsc's documented `x-named-type` collision caveat. If a shared-model
|
|
165
|
-
* name (referenced via `x-named-type`) also matches the name ajsc derives for a
|
|
166
|
-
* sibling *structural* sub-type, ajsc silently MERGES them — the model reference
|
|
167
|
-
* resolves to the unrelated structural type. The merge happens inside ajsc's own
|
|
168
|
-
* pass, so it can't be disentangled afterward (a post-hoc rename would rewrite
|
|
169
|
-
* the model reference too, producing a silently-wrong type). Instead we detect
|
|
170
|
-
* the collision — a name present in BOTH `referencedNamedTypes` AND
|
|
171
|
-
* `extractedTypeNames` (ajsc's own documented collision signal) — and fail fast
|
|
172
|
-
* with an actionable message. The throw is wrapped with the route's name/scope
|
|
173
|
-
* by `emitScopeFile`.
|
|
174
|
-
*/
|
|
175
|
-
function assertNoModelNameCollision(result: ExtractedTypeOutput): void {
|
|
176
|
-
if (result.referencedNamedTypes.length === 0 || result.extractedTypeNames.length === 0) return
|
|
177
|
-
const extracted = new Set(result.extractedTypeNames)
|
|
178
|
-
const collisions = result.referencedNamedTypes.filter((name) => extracted.has(name))
|
|
179
|
-
if (collisions.length === 0) return
|
|
180
|
-
const names = collisions.map((n) => `'${n}'`).join(', ')
|
|
181
|
-
throw new Error(
|
|
182
|
-
`shared model ${names} collides with a generated type of the same name derived ` +
|
|
183
|
-
`from a property in this route's schema — ajsc would silently merge them. Rename ` +
|
|
184
|
-
`the colliding property, or change the model's $id/title so the generated names differ`,
|
|
185
|
-
)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Drop-in replacement for `jsonSchemaToTypeBody` mirroring {@link convertExtracted}
|
|
190
|
-
* for the bare-body (flat / merged-alias) conversion path.
|
|
191
|
-
*/
|
|
192
|
-
async function convertBody(
|
|
193
|
-
schema: Record<string, unknown>,
|
|
194
|
-
ctx: EmitRouteContext,
|
|
195
|
-
): Promise<string | undefined> {
|
|
196
|
-
if (ctx.idToModelName == null || ctx.idToModelName.size === 0) {
|
|
197
|
-
return jsonSchemaToTypeBody(schema, ctx.ajsc)
|
|
198
|
-
}
|
|
199
|
-
const { schema: sub } = substituteModelRefs(schema, ctx.idToModelName)
|
|
200
|
-
const result = await jsonSchemaToTypeBodyWithRefs(sub, ctx.ajsc)
|
|
201
|
-
if (result == null) return undefined
|
|
202
|
-
for (const name of result.referencedNamedTypes) ctx.referencedModels.add(name)
|
|
203
|
-
return result.body
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Tracks extracted declarations emitted into a single namespace, guarding
|
|
208
|
-
* against duplicate identifiers (defense-in-depth on top of the rename pass).
|
|
209
|
-
*
|
|
210
|
-
* - Exact-string duplicates (the same sub-type extracted from two schemas) are
|
|
211
|
-
* silently skipped.
|
|
212
|
-
* - A same-name-but-different-body declaration is a genuine collision the
|
|
213
|
-
* rename pass failed to resolve; emitting it would produce an opaque
|
|
214
|
-
* `TS2300: Duplicate identifier` in the consumer's build. We fail fast at
|
|
215
|
-
* codegen with a message that names the offending identifier instead.
|
|
216
|
-
*
|
|
217
|
-
* Returns the indented declaration line to push, or `null` when it should be
|
|
218
|
-
* skipped (exact duplicate).
|
|
219
|
-
*/
|
|
220
|
-
class DeclarationCollector {
|
|
221
|
-
private readonly seenStrings = new Set<string>()
|
|
222
|
-
private readonly seenNames = new Map<string, string>()
|
|
223
|
-
|
|
224
|
-
constructor(private readonly context: string) {}
|
|
225
|
-
|
|
226
|
-
/** Returns the indented line to emit, or `null` for an exact duplicate. */
|
|
227
|
-
accept(decl: string, indentPrefix: string): string | null {
|
|
228
|
-
if (this.seenStrings.has(decl)) return null
|
|
229
|
-
const name = extractedDeclName(decl)
|
|
230
|
-
if (name != null) {
|
|
231
|
-
if (this.seenNames.has(name)) {
|
|
232
|
-
throw new Error(
|
|
233
|
-
`[ts-procedures-codegen] duplicate identifier '${name}' while emitting ${this.context}. ` +
|
|
234
|
-
`An extracted sub-type collided with another of the same name and could not be renamed. ` +
|
|
235
|
-
`This is a codegen bug — please report it with the offending schema.`,
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
this.seenNames.set(name, decl)
|
|
239
|
-
}
|
|
240
|
-
this.seenStrings.add(decl)
|
|
241
|
-
return indent(decl, indentPrefix)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
interface NamedType {
|
|
246
|
-
/** Short name for namespace mode (e.g., 'Params', 'Response'). */
|
|
247
|
-
shortName: string
|
|
248
|
-
/** Schema to convert, or undefined if not present. */
|
|
249
|
-
schema: Record<string, unknown> | undefined
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
interface FormattedTypes {
|
|
253
|
-
/** Type declarations to add to the file. */
|
|
254
|
-
declarations: string[]
|
|
255
|
-
/** Map of shortName → qualified type reference (for callables). */
|
|
256
|
-
refs: Record<string, string>
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Converts multiple schemas into type declarations and type references.
|
|
261
|
-
* In flat mode: `export type ${routePascal}${shortName} = <body>`
|
|
262
|
-
* In namespace mode: extracted sub-types + named types inside `export namespace ${routePascal} { ... }`
|
|
263
|
-
*
|
|
264
|
-
* `extraReserved` lets the caller pre-reserve identifier names that the route
|
|
265
|
-
* will inject AFTER formatTypes returns (e.g. emitApiRoute's structured
|
|
266
|
-
* `Params`). Without this, an ajsc-extracted sub-type could shadow the
|
|
267
|
-
* injected one and produce duplicate `export type Params` declarations.
|
|
268
|
-
*/
|
|
269
|
-
async function formatTypes(
|
|
270
|
-
routePascal: string,
|
|
271
|
-
types: NamedType[],
|
|
272
|
-
ctx: EmitRouteContext,
|
|
273
|
-
extraReserved?: ReadonlySet<string>,
|
|
274
|
-
): Promise<FormattedTypes> {
|
|
275
|
-
const declarations: string[] = []
|
|
276
|
-
const refs: Record<string, string> = {}
|
|
277
|
-
|
|
278
|
-
if (ctx.namespaceTypes) {
|
|
279
|
-
const nsLines: string[] = []
|
|
280
|
-
const collector = new DeclarationCollector(`namespace ${routePascal}`)
|
|
281
|
-
|
|
282
|
-
// Pre-reserve every name the route will declare itself (each shortName +
|
|
283
|
-
// any caller-supplied extras). Extracted sub-types whose names land in
|
|
284
|
-
// this set get renamed (e.g. `Params` → `Params_`) by renameExtractedTypes,
|
|
285
|
-
// and the body string is patched in lockstep so the reference still
|
|
286
|
-
// resolves. The set is mutated as we go, so a sub-type renamed in schema A
|
|
287
|
-
// is also reserved against schema B.
|
|
288
|
-
const taken = new Set<string>(extraReserved ?? [])
|
|
289
|
-
for (const t of types) {
|
|
290
|
-
if (t.schema != null) taken.add(t.shortName)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
for (const { shortName, schema } of types) {
|
|
294
|
-
if (schema == null) continue
|
|
295
|
-
|
|
296
|
-
const rawResult = await convertExtracted(schema, ctx)
|
|
297
|
-
if (rawResult == null) continue
|
|
298
|
-
|
|
299
|
-
const result = renameExtractedTypes(rawResult, taken)
|
|
300
|
-
|
|
301
|
-
// Collect extracted sub-types (dedupe exact dups; throw on real collisions)
|
|
302
|
-
for (const decl of result.declarations) {
|
|
303
|
-
const line = collector.accept(decl, ' ')
|
|
304
|
-
if (line != null) nsLines.push(line)
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
nsLines.push(` export type ${shortName} = ${result.body}`)
|
|
308
|
-
refs[shortName] = `${ctx.scopePascal}.${routePascal}.${shortName}`
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (nsLines.length > 0) {
|
|
312
|
-
declarations.push(` export namespace ${routePascal} {\n${nsLines.join('\n')}\n }`)
|
|
313
|
-
}
|
|
314
|
-
} else {
|
|
315
|
-
for (const { shortName, schema } of types) {
|
|
316
|
-
if (schema == null) continue
|
|
317
|
-
|
|
318
|
-
const flatName = `${routePascal}${shortName}`
|
|
319
|
-
const body = await convertBody(schema, ctx)
|
|
320
|
-
if (body == null) continue
|
|
321
|
-
|
|
322
|
-
declarations.push(`export type ${flatName} = ${body}`)
|
|
323
|
-
refs[shortName] = flatName
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return { declarations, refs }
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ---------------------------------------------------------------------------
|
|
331
|
-
// Route-level Errors union injection
|
|
332
|
-
// ---------------------------------------------------------------------------
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Builds the body of an `Errors` type union from the route's declared error
|
|
336
|
-
* keys. Filters to keys actually emitted in `_errors.ts` so generated code
|
|
337
|
-
* never references undefined types.
|
|
338
|
-
*
|
|
339
|
-
* In namespace mode the union uses qualified names (`ApiErrors.UseCaseError`);
|
|
340
|
-
* in flat mode it uses the bundled wildcard import alias (`_errors.UseCaseError`).
|
|
341
|
-
* Returns `null` when no keys remain.
|
|
342
|
-
*/
|
|
343
|
-
function buildErrorUnion(
|
|
344
|
-
routeErrors: string[] | undefined,
|
|
345
|
-
ctx: EmitRouteContext
|
|
346
|
-
): string | null {
|
|
347
|
-
if (!routeErrors || routeErrors.length === 0) return null
|
|
348
|
-
const available = ctx.errorKeys
|
|
349
|
-
const filtered = available ? routeErrors.filter((k) => available.has(k)) : routeErrors
|
|
350
|
-
if (filtered.length === 0) return null
|
|
351
|
-
const qualify = ctx.namespaceTypes
|
|
352
|
-
? (k: string) => `${toPascalCase(ctx.serviceName)}Errors.${k}`
|
|
353
|
-
: (k: string) => `_errors.${k}`
|
|
354
|
-
return filtered.map(qualify).join(' | ')
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Injects `export type Errors = ...` into an existing route namespace block
|
|
359
|
-
* (namespace mode) or appends a flat `export type ${pascal}Errors = ...` in
|
|
360
|
-
* flat mode. Mutates the `declarations` array in place and returns whether an
|
|
361
|
-
* injection happened.
|
|
362
|
-
*/
|
|
363
|
-
function injectRouteErrors(
|
|
364
|
-
declarations: string[],
|
|
365
|
-
routePascal: string,
|
|
366
|
-
errorUnion: string | null,
|
|
367
|
-
namespaceTypes: boolean
|
|
368
|
-
): boolean {
|
|
369
|
-
if (!errorUnion) return false
|
|
370
|
-
if (namespaceTypes) {
|
|
371
|
-
const lastIdx = declarations.length - 1
|
|
372
|
-
if (lastIdx < 0) return false
|
|
373
|
-
const lastDecl = declarations[lastIdx]!
|
|
374
|
-
const closingIdx = lastDecl.lastIndexOf(' }')
|
|
375
|
-
if (closingIdx === -1) return false
|
|
376
|
-
declarations[lastIdx] =
|
|
377
|
-
lastDecl.slice(0, closingIdx) +
|
|
378
|
-
` export type Errors = ${errorUnion}\n` +
|
|
379
|
-
lastDecl.slice(closingIdx)
|
|
380
|
-
return true
|
|
381
|
-
}
|
|
382
|
-
declarations.push(`export type ${routePascal}Errors = ${errorUnion}`)
|
|
383
|
-
return true
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// ---------------------------------------------------------------------------
|
|
387
|
-
// Route emitters
|
|
388
|
-
// ---------------------------------------------------------------------------
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Builds the multi-line JSDoc comment for a route callable. Surfaces the
|
|
392
|
-
* second `options` argument (the per-call AbortSignal/timeout seam — DX #8) and,
|
|
393
|
-
* for routes that declare typed errors, points at the route's `Errors` type and
|
|
394
|
-
* how to narrow it on the throwing path (DX #10). Indented for placement inside
|
|
395
|
-
* the bind-object (4 spaces).
|
|
396
|
-
*/
|
|
397
|
-
function buildCallableJsDoc(opts: {
|
|
398
|
-
methodLabel: string
|
|
399
|
-
path: string
|
|
400
|
-
errorsRef: string | null
|
|
401
|
-
}): string {
|
|
402
|
-
const lines = [
|
|
403
|
-
` /**`,
|
|
404
|
-
` * ${opts.methodLabel} ${opts.path}`,
|
|
405
|
-
` *`,
|
|
406
|
-
` * @param options Optional per-call {@link ProcedureCallOptions} —`,
|
|
407
|
-
` * \`signal\` (cancel on dispose), \`timeout\`, \`headers\`, \`basePath\`.`,
|
|
408
|
-
]
|
|
409
|
-
if (opts.errorsRef) {
|
|
410
|
-
lines.push(
|
|
411
|
-
` * @throws Declared typed errors: {@link ${opts.errorsRef}}. Narrow with`,
|
|
412
|
-
` * \`instanceof\` on the throwing path, or call \`.safe()\` for a \`Result\`.`,
|
|
413
|
-
)
|
|
414
|
-
}
|
|
415
|
-
lines.push(` */`)
|
|
416
|
-
return lines.join('\n')
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async function emitRpcRoute(route: RPCHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
420
|
-
const pascal = versionedPascal(route.name, route.version)
|
|
421
|
-
|
|
422
|
-
const { declarations, refs } = await formatTypes(pascal, [
|
|
423
|
-
{ shortName: 'Params', schema: route.jsonSchema.body },
|
|
424
|
-
{ shortName: 'Response', schema: route.jsonSchema.response },
|
|
425
|
-
], ctx)
|
|
426
|
-
|
|
427
|
-
const paramsTypeName = refs['Params'] ?? 'void'
|
|
428
|
-
const responseTypeName = refs['Response'] ?? 'unknown'
|
|
429
|
-
const scopeStr = Array.isArray(route.scope) ? route.scope.join('-') : route.scope
|
|
430
|
-
|
|
431
|
-
const errorUnion = buildErrorUnion(route.errors, ctx)
|
|
432
|
-
const hasErrors = errorUnion !== null
|
|
433
|
-
const errorsRef = ctx.namespaceTypes
|
|
434
|
-
? `${ctx.scopePascal}.${pascal}.Errors`
|
|
435
|
-
: `${pascal}Errors`
|
|
436
|
-
const helperCall = hasErrors
|
|
437
|
-
? `client.bindCallableTyped<${paramsTypeName}, ${responseTypeName}, ${errorsRef}>`
|
|
438
|
-
: `client.bindCallable<${paramsTypeName}, ${responseTypeName}>`
|
|
439
|
-
|
|
440
|
-
const callable = [
|
|
441
|
-
buildCallableJsDoc({
|
|
442
|
-
methodLabel: route.method.toUpperCase(),
|
|
443
|
-
path: route.path,
|
|
444
|
-
errorsRef: hasErrors ? errorsRef : null,
|
|
445
|
-
}),
|
|
446
|
-
` ${pascal}: ${helperCall}({`,
|
|
447
|
-
` name: '${pascal}',`,
|
|
448
|
-
` scope: '${scopeStr}',`,
|
|
449
|
-
` path: '${route.path}',`,
|
|
450
|
-
` method: '${route.method}',`,
|
|
451
|
-
` kind: 'rpc',`,
|
|
452
|
-
` }),`,
|
|
453
|
-
].join('\n')
|
|
454
|
-
|
|
455
|
-
const hasErrorsInjected = injectRouteErrors(declarations, pascal, errorUnion, ctx.namespaceTypes)
|
|
456
|
-
|
|
457
|
-
return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Formats a group of named types into a nested sub-namespace block (for namespace mode).
|
|
462
|
-
* Returns an array of lines to be inserted into the parent namespace, and a map of
|
|
463
|
-
* shortName → qualified type reference for use in callables.
|
|
464
|
-
*
|
|
465
|
-
* In flat mode, returns declarations like `export type ${prefix}${shortName} = ...`
|
|
466
|
-
* and refs like `${prefix}${shortName}`.
|
|
467
|
-
*/
|
|
468
|
-
async function formatSubNamespace(
|
|
469
|
-
routePascal: string,
|
|
470
|
-
nsName: string, // e.g. 'Req' or 'Response'
|
|
471
|
-
types: NamedType[],
|
|
472
|
-
ctx: EmitRouteContext,
|
|
473
|
-
taken: Set<string>,
|
|
474
|
-
): Promise<{ nsBlock: string | null; refs: Record<string, string> }> {
|
|
475
|
-
const refs: Record<string, string> = {}
|
|
476
|
-
const nsLines: string[] = []
|
|
477
|
-
const collector = new DeclarationCollector(`namespace ${routePascal}.${nsName}`)
|
|
478
|
-
|
|
479
|
-
// Pre-reserve short names to prevent sub-type extraction collision
|
|
480
|
-
for (const t of types) {
|
|
481
|
-
if (t.schema != null) taken.add(t.shortName)
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
for (const { shortName, schema } of types) {
|
|
485
|
-
if (schema == null) continue
|
|
486
|
-
|
|
487
|
-
if (ctx.namespaceTypes) {
|
|
488
|
-
const rawResult = await convertExtracted(schema, ctx)
|
|
489
|
-
if (rawResult == null) continue
|
|
490
|
-
|
|
491
|
-
const result = renameExtractedTypes(rawResult, taken)
|
|
492
|
-
|
|
493
|
-
for (const decl of result.declarations) {
|
|
494
|
-
const line = collector.accept(decl, ' ')
|
|
495
|
-
if (line != null) nsLines.push(line)
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
nsLines.push(` export type ${shortName} = ${result.body}`)
|
|
499
|
-
refs[shortName] = `${ctx.scopePascal}.${routePascal}.${nsName}.${shortName}`
|
|
500
|
-
} else {
|
|
501
|
-
const flatName = `${routePascal}${nsName}${shortName}`
|
|
502
|
-
const body = await convertBody(schema, ctx)
|
|
503
|
-
if (body == null) continue
|
|
504
|
-
refs[shortName] = flatName
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (ctx.namespaceTypes) {
|
|
509
|
-
if (nsLines.length === 0) return { nsBlock: null, refs }
|
|
510
|
-
const nsBlock = ` export namespace ${nsName} {\n${nsLines.join('\n')}\n }`
|
|
511
|
-
return { nsBlock, refs }
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return { nsBlock: null, refs }
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* Builds the conditional return type string for an API or http-stream callable.
|
|
519
|
-
*
|
|
520
|
-
* - Both body + headers → `{ body: <Body>; headers: <Headers> }`
|
|
521
|
-
* - Only body → `<Body>`
|
|
522
|
-
* - Only headers → `{ headers: <Headers> }`
|
|
523
|
-
* - Neither → `void`
|
|
524
|
-
*/
|
|
525
|
-
function buildApiReturnType(
|
|
526
|
-
bodyRef: string | undefined,
|
|
527
|
-
headersRef: string | undefined,
|
|
528
|
-
): string {
|
|
529
|
-
if (bodyRef && headersRef) {
|
|
530
|
-
return `{ body: ${bodyRef}; headers: ${headersRef} }`
|
|
531
|
-
}
|
|
532
|
-
if (bodyRef) return bodyRef
|
|
533
|
-
if (headersRef) return `{ headers: ${headersRef} }`
|
|
534
|
-
return 'void'
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
async function emitApiRoute(route: APIHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
538
|
-
const pascal = toPascalCase(route.name)
|
|
539
|
-
const req = route.jsonSchema.req ?? {}
|
|
540
|
-
const res = route.jsonSchema.res ?? {}
|
|
541
|
-
|
|
542
|
-
// Request channels
|
|
543
|
-
const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
|
|
544
|
-
const reqTypes: NamedType[] = []
|
|
545
|
-
const presentChannels: string[] = []
|
|
546
|
-
|
|
547
|
-
for (const channel of reqChannelKeys) {
|
|
548
|
-
const schema = req[channel]
|
|
549
|
-
if (schema != null) {
|
|
550
|
-
reqTypes.push({ shortName: toPascalCase(channel), schema })
|
|
551
|
-
presentChannels.push(channel)
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// Response slots
|
|
556
|
-
const resTypes: NamedType[] = [
|
|
557
|
-
{ shortName: 'Body', schema: res.body },
|
|
558
|
-
{ shortName: 'Headers', schema: res.headers },
|
|
559
|
-
]
|
|
560
|
-
|
|
561
|
-
const scopeStr = route.scope ?? 'default'
|
|
562
|
-
const errorUnion = buildErrorUnion(route.errors, ctx)
|
|
563
|
-
const hasErrors = errorUnion !== null
|
|
564
|
-
const errorsRef = ctx.namespaceTypes
|
|
565
|
-
? `${ctx.scopePascal}.${pascal}.Errors`
|
|
566
|
-
: `${pascal}Errors`
|
|
567
|
-
|
|
568
|
-
const declarations: string[] = []
|
|
569
|
-
let paramsTypeName = 'void'
|
|
570
|
-
let returnTypeName = 'void'
|
|
571
|
-
|
|
572
|
-
// Track reserved names across all sub-namespaces. Model names are reserved
|
|
573
|
-
// so an ajsc-extracted sub-type can't silently merge with a referenced model.
|
|
574
|
-
const taken = new Set<string>(['Req', 'Response'])
|
|
575
|
-
|
|
576
|
-
if (ctx.namespaceTypes) {
|
|
577
|
-
// Namespace mode: emit nested Req {} and Response {} namespaces inside route namespace.
|
|
578
|
-
// Also emit merged type aliases `export type Req = { ... }` and `export type Response = ...`
|
|
579
|
-
// so they can be used as type arguments to bindCallable (TS requires a TYPE, not a namespace).
|
|
580
|
-
const nsLines: string[] = []
|
|
581
|
-
|
|
582
|
-
const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
|
|
583
|
-
pascal, 'Req', reqTypes, ctx, taken
|
|
584
|
-
)
|
|
585
|
-
if (reqBlock) {
|
|
586
|
-
nsLines.push(reqBlock)
|
|
587
|
-
// Merged type alias for Req so it can be used as a generic type arg
|
|
588
|
-
const reqFields = presentChannels
|
|
589
|
-
.map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
|
|
590
|
-
.join('; ')
|
|
591
|
-
nsLines.push(` export type Req = { ${reqFields} }`)
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
const { nsBlock: resBlock, refs: resRefs } = await formatSubNamespace(
|
|
595
|
-
pascal, 'Response', resTypes, ctx, taken
|
|
596
|
-
)
|
|
597
|
-
if (resBlock) {
|
|
598
|
-
nsLines.push(resBlock)
|
|
599
|
-
// No merged Response type alias needed: we reference Response.Body / Response.Headers
|
|
600
|
-
// directly in the return type string, which are namespace-qualified paths (valid).
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// Emit Errors type last (injected by injectRouteErrors below)
|
|
604
|
-
// Build the route namespace block
|
|
605
|
-
if (nsLines.length > 0) {
|
|
606
|
-
declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n\n')}\n }`)
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Params type: use the merged Req type alias
|
|
610
|
-
if (presentChannels.length > 0) {
|
|
611
|
-
paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Return type
|
|
615
|
-
const bodyRef = resRefs['Body']
|
|
616
|
-
const headersRef = resRefs['Headers']
|
|
617
|
-
returnTypeName = buildApiReturnType(bodyRef, headersRef)
|
|
618
|
-
} else {
|
|
619
|
-
// Flat mode: emit individual types prefixed with route + sub-namespace name
|
|
620
|
-
for (const { shortName, schema } of reqTypes) {
|
|
621
|
-
if (schema == null) continue
|
|
622
|
-
const flatName = `${pascal}Req${shortName}`
|
|
623
|
-
const body = await convertBody(schema, ctx)
|
|
624
|
-
if (body == null) continue
|
|
625
|
-
declarations.push(`export type ${flatName} = ${body}`)
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// Flat mode: compose structured Req type
|
|
629
|
-
if (presentChannels.length > 0) {
|
|
630
|
-
const structureFields = presentChannels
|
|
631
|
-
.map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
|
|
632
|
-
.join('; ')
|
|
633
|
-
declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
|
|
634
|
-
paramsTypeName = `${pascal}Req`
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// Flat mode: emit response types
|
|
638
|
-
let bodyRef: string | undefined
|
|
639
|
-
let headersRef: string | undefined
|
|
640
|
-
|
|
641
|
-
for (const { shortName, schema } of resTypes) {
|
|
642
|
-
if (schema == null) continue
|
|
643
|
-
const flatName = `${pascal}Response${shortName}`
|
|
644
|
-
const body = await convertBody(schema, ctx)
|
|
645
|
-
if (body == null) continue
|
|
646
|
-
declarations.push(`export type ${flatName} = ${body}`)
|
|
647
|
-
if (shortName === 'Body') bodyRef = flatName
|
|
648
|
-
if (shortName === 'Headers') headersRef = flatName
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
returnTypeName = buildApiReturnType(bodyRef, headersRef)
|
|
652
|
-
|
|
653
|
-
// Flat mode errors
|
|
654
|
-
if (errorUnion) {
|
|
655
|
-
declarations.push(`export type ${pascal}Errors = ${errorUnion}`)
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const responseHeadersDeclared = res.headers != null
|
|
660
|
-
const helperCall = hasErrors
|
|
661
|
-
? `client.bindCallableTyped<${paramsTypeName}, ${returnTypeName}, ${errorsRef}>`
|
|
662
|
-
: `client.bindCallable<${paramsTypeName}, ${returnTypeName}>`
|
|
663
|
-
|
|
664
|
-
const descriptorLines = [
|
|
665
|
-
` name: '${route.name}',`,
|
|
666
|
-
` scope: '${scopeStr}',`,
|
|
667
|
-
` path: '${route.fullPath}',`,
|
|
668
|
-
` method: '${route.method}',`,
|
|
669
|
-
` kind: 'api',`,
|
|
670
|
-
...(responseHeadersDeclared ? [` responseHeadersDeclared: true,`] : []),
|
|
671
|
-
]
|
|
672
|
-
|
|
673
|
-
const callable = [
|
|
674
|
-
buildCallableJsDoc({
|
|
675
|
-
methodLabel: route.method.toUpperCase(),
|
|
676
|
-
path: route.fullPath,
|
|
677
|
-
errorsRef: hasErrors ? errorsRef : null,
|
|
678
|
-
}),
|
|
679
|
-
` ${route.name}: ${helperCall}({`,
|
|
680
|
-
...descriptorLines,
|
|
681
|
-
` }),`,
|
|
682
|
-
].join('\n')
|
|
683
|
-
|
|
684
|
-
const hasErrorsInjected = ctx.namespaceTypes
|
|
685
|
-
? injectRouteErrors(declarations, pascal, errorUnion, ctx.namespaceTypes)
|
|
686
|
-
: errorUnion !== null // flat mode already emitted errors above
|
|
687
|
-
|
|
688
|
-
return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
async function emitHttpStreamRoute(route: HttpStreamRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
692
|
-
const pascal = toPascalCase(route.name)
|
|
693
|
-
const req = route.jsonSchema.req ?? {}
|
|
694
|
-
const res = route.jsonSchema.res ?? {}
|
|
695
|
-
|
|
696
|
-
// Request channels
|
|
697
|
-
const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
|
|
698
|
-
const reqTypes: NamedType[] = []
|
|
699
|
-
const presentChannels: string[] = []
|
|
700
|
-
|
|
701
|
-
for (const channel of reqChannelKeys) {
|
|
702
|
-
const schema = req[channel]
|
|
703
|
-
if (schema != null) {
|
|
704
|
-
reqTypes.push({ shortName: toPascalCase(channel), schema })
|
|
705
|
-
presentChannels.push(channel)
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// Yield + ReturnType
|
|
710
|
-
const yieldSchema = route.jsonSchema.yield
|
|
711
|
-
const returnSchema = route.jsonSchema.returnType
|
|
712
|
-
const resHeadersSchema = res.headers
|
|
713
|
-
|
|
714
|
-
const scopeStr = route.scope ?? 'default'
|
|
715
|
-
const declarations: string[] = []
|
|
716
|
-
let paramsTypeName = 'void'
|
|
717
|
-
let yieldTypeName = 'unknown'
|
|
718
|
-
let returnTypeName = 'void'
|
|
719
|
-
|
|
720
|
-
const taken = new Set<string>(['Req', 'Response', 'Yield', 'ReturnType'])
|
|
721
|
-
|
|
722
|
-
if (ctx.namespaceTypes) {
|
|
723
|
-
const nsLines: string[] = []
|
|
724
|
-
|
|
725
|
-
// Req sub-namespace
|
|
726
|
-
const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
|
|
727
|
-
pascal, 'Req', reqTypes, ctx, taken
|
|
728
|
-
)
|
|
729
|
-
if (reqBlock) {
|
|
730
|
-
nsLines.push(reqBlock)
|
|
731
|
-
// Merged type alias so Req can be used as a generic type arg (same pattern as emitApiRoute)
|
|
732
|
-
const reqFields = presentChannels
|
|
733
|
-
.map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
|
|
734
|
-
.join('; ')
|
|
735
|
-
nsLines.push(` export type Req = { ${reqFields} }`)
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// Response sub-namespace (headers only for http-stream)
|
|
739
|
-
const resTypes: NamedType[] = [{ shortName: 'Headers', schema: resHeadersSchema }]
|
|
740
|
-
const { nsBlock: resBlock } = await formatSubNamespace(
|
|
741
|
-
pascal, 'Response', resTypes, ctx, taken
|
|
742
|
-
)
|
|
743
|
-
if (resBlock) nsLines.push(resBlock)
|
|
744
|
-
|
|
745
|
-
// Yield and ReturnType directly in the route namespace
|
|
746
|
-
const directTypes: NamedType[] = [
|
|
747
|
-
{ shortName: 'Yield', schema: yieldSchema },
|
|
748
|
-
{ shortName: 'ReturnType', schema: returnSchema },
|
|
749
|
-
]
|
|
750
|
-
const collector = new DeclarationCollector(`namespace ${pascal}`)
|
|
751
|
-
for (const { shortName, schema } of directTypes) {
|
|
752
|
-
if (schema == null) continue
|
|
753
|
-
const rawResult = await convertExtracted(schema, ctx)
|
|
754
|
-
if (rawResult == null) continue
|
|
755
|
-
const result = renameExtractedTypes(rawResult, taken)
|
|
756
|
-
for (const decl of result.declarations) {
|
|
757
|
-
const line = collector.accept(decl, ' ')
|
|
758
|
-
if (line != null) nsLines.push(line)
|
|
759
|
-
}
|
|
760
|
-
nsLines.push(` export type ${shortName} = ${result.body}`)
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
if (nsLines.length > 0) {
|
|
764
|
-
declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n')}\n }`)
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
if (presentChannels.length > 0) {
|
|
768
|
-
paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
if (yieldSchema != null) yieldTypeName = `${ctx.scopePascal}.${pascal}.Yield`
|
|
772
|
-
if (returnSchema != null) returnTypeName = `${ctx.scopePascal}.${pascal}.ReturnType`
|
|
773
|
-
} else {
|
|
774
|
-
// Flat mode
|
|
775
|
-
for (const { shortName, schema } of reqTypes) {
|
|
776
|
-
if (schema == null) continue
|
|
777
|
-
const flatName = `${pascal}Req${shortName}`
|
|
778
|
-
const body = await convertBody(schema, ctx)
|
|
779
|
-
if (body == null) continue
|
|
780
|
-
declarations.push(`export type ${flatName} = ${body}`)
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
if (presentChannels.length > 0) {
|
|
784
|
-
const structureFields = presentChannels
|
|
785
|
-
.map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
|
|
786
|
-
.join('; ')
|
|
787
|
-
declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
|
|
788
|
-
paramsTypeName = `${pascal}Req`
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
if (resHeadersSchema != null) {
|
|
792
|
-
const body = await convertBody(resHeadersSchema, ctx)
|
|
793
|
-
if (body != null) declarations.push(`export type ${pascal}ResponseHeaders = ${body}`)
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
if (yieldSchema != null) {
|
|
797
|
-
const body = await convertBody(yieldSchema, ctx)
|
|
798
|
-
if (body != null) {
|
|
799
|
-
declarations.push(`export type ${pascal}Yield = ${body}`)
|
|
800
|
-
yieldTypeName = `${pascal}Yield`
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
if (returnSchema != null) {
|
|
805
|
-
const body = await convertBody(returnSchema, ctx)
|
|
806
|
-
if (body != null) {
|
|
807
|
-
declarations.push(`export type ${pascal}ReturnType = ${body}`)
|
|
808
|
-
returnTypeName = `${pascal}ReturnType`
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
const callable = [
|
|
814
|
-
buildCallableJsDoc({
|
|
815
|
-
methodLabel: route.method.toUpperCase(),
|
|
816
|
-
path: route.fullPath,
|
|
817
|
-
errorsRef: null,
|
|
818
|
-
}),
|
|
819
|
-
` ${route.name}(req: ${paramsTypeName}, options?: ProcedureCallOptions): TypedStream<${yieldTypeName}, ${returnTypeName}> {`,
|
|
820
|
-
` return client.stream<${yieldTypeName}, ${returnTypeName}>({`,
|
|
821
|
-
` name: '${route.name}',`,
|
|
822
|
-
` scope: '${scopeStr}',`,
|
|
823
|
-
` path: '${route.fullPath}',`,
|
|
824
|
-
` method: '${route.method}',`,
|
|
825
|
-
` kind: 'http-stream',`,
|
|
826
|
-
` streamMode: '${route.streamMode}',`,
|
|
827
|
-
` params: req,`,
|
|
828
|
-
` }, options)`,
|
|
829
|
-
` },`,
|
|
830
|
-
].join('\n')
|
|
831
|
-
|
|
832
|
-
const hasErrors = injectRouteErrors(
|
|
833
|
-
declarations,
|
|
834
|
-
pascal,
|
|
835
|
-
buildErrorUnion(route.errors, ctx),
|
|
836
|
-
ctx.namespaceTypes
|
|
837
|
-
)
|
|
838
|
-
|
|
839
|
-
return { typeDeclarations: declarations, callable, hasStream: true, hasErrors }
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
async function emitStreamRoute(route: StreamHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
|
|
843
|
-
const pascal = versionedPascal(route.name, route.version)
|
|
844
|
-
|
|
845
|
-
// Unwrap SSE envelope from yieldType
|
|
846
|
-
let yieldSchema = route.jsonSchema.yieldType
|
|
847
|
-
if (yieldSchema != null && route.streamMode === 'sse' && isSseEnvelope(yieldSchema)) {
|
|
848
|
-
yieldSchema = unwrapSseEnvelope(yieldSchema)
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const { declarations, refs } = await formatTypes(pascal, [
|
|
852
|
-
{ shortName: 'Params', schema: route.jsonSchema.params },
|
|
853
|
-
{ shortName: 'Yield', schema: yieldSchema },
|
|
854
|
-
{ shortName: 'Return', schema: route.jsonSchema.returnType },
|
|
855
|
-
], ctx)
|
|
856
|
-
|
|
857
|
-
const paramsTypeName = refs['Params'] ?? 'void'
|
|
858
|
-
const yieldTypeName = refs['Yield'] ?? 'unknown'
|
|
859
|
-
const returnTypeName = refs['Return'] ?? 'void'
|
|
860
|
-
const scopeStr = Array.isArray(route.scope) ? route.scope.join('-') : route.scope
|
|
861
|
-
|
|
862
|
-
const callable = [
|
|
863
|
-
buildCallableJsDoc({
|
|
864
|
-
methodLabel: route.methods.map((m) => m.toUpperCase()).join('|'),
|
|
865
|
-
path: route.path,
|
|
866
|
-
errorsRef: null,
|
|
867
|
-
}),
|
|
868
|
-
` ${pascal}(params: ${paramsTypeName}, options?: ProcedureCallOptions): TypedStream<${yieldTypeName}, ${returnTypeName}> {`,
|
|
869
|
-
` return client.stream<${yieldTypeName}, ${returnTypeName}>({`,
|
|
870
|
-
` name: '${pascal}',`,
|
|
871
|
-
` scope: '${scopeStr}',`,
|
|
872
|
-
` path: '${route.path}',`,
|
|
873
|
-
` method: '${route.methods[0] ?? 'get'}',`,
|
|
874
|
-
` kind: 'stream',`,
|
|
875
|
-
` streamMode: '${route.streamMode}',`,
|
|
876
|
-
` params,`,
|
|
877
|
-
` }, options)`,
|
|
878
|
-
` },`,
|
|
879
|
-
].join('\n')
|
|
880
|
-
|
|
881
|
-
const hasErrors = injectRouteErrors(
|
|
882
|
-
declarations,
|
|
883
|
-
pascal,
|
|
884
|
-
buildErrorUnion(route.errors, ctx),
|
|
885
|
-
ctx.namespaceTypes
|
|
886
|
-
)
|
|
887
|
-
|
|
888
|
-
return { typeDeclarations: declarations, callable, hasStream: true, hasErrors }
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// ---------------------------------------------------------------------------
|
|
892
|
-
// emitScopeFile
|
|
893
|
-
// ---------------------------------------------------------------------------
|
|
894
|
-
|
|
895
|
-
/**
|
|
896
|
-
* Generates a complete TypeScript scope file for a ScopeGroup.
|
|
897
|
-
*
|
|
898
|
-
* When `namespaceTypes` is true, types are wrapped in nested TypeScript namespaces
|
|
899
|
-
* and ajsc runs with `inlineTypes: false` so formatting options (enumStyle, depluralize,
|
|
900
|
-
* jsdoc, etc.) produce extracted sub-types inside each namespace.
|
|
901
|
-
*/
|
|
902
|
-
export async function emitScopeFile(
|
|
903
|
-
group: ScopeGroup,
|
|
904
|
-
options?: EmitScopeOptions,
|
|
905
|
-
): Promise<string> {
|
|
906
|
-
const {
|
|
907
|
-
ajsc: ajscOpts,
|
|
908
|
-
clientImportPath = 'ts-procedures/client',
|
|
909
|
-
namespaceTypes = false,
|
|
910
|
-
serviceName = 'Api',
|
|
911
|
-
errorKeys,
|
|
912
|
-
idToModelName,
|
|
913
|
-
} = options ?? {}
|
|
914
|
-
|
|
915
|
-
const pascal = toPascalCase(group.camelCase)
|
|
916
|
-
const referencedModels = new Set<string>()
|
|
917
|
-
const ctx: EmitRouteContext = {
|
|
918
|
-
ajsc: ajscOpts,
|
|
919
|
-
namespaceTypes,
|
|
920
|
-
scopePascal: pascal,
|
|
921
|
-
serviceName,
|
|
922
|
-
errorKeys,
|
|
923
|
-
idToModelName,
|
|
924
|
-
referencedModels,
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
const allTypeDeclarations: string[] = []
|
|
928
|
-
const callables: string[] = []
|
|
929
|
-
let hasStream = false
|
|
930
|
-
let scopeHasErrors = false
|
|
931
|
-
|
|
932
|
-
for (const route of group.routes) {
|
|
933
|
-
let chunks: RouteChunks
|
|
934
|
-
const kind = route.kind ?? inferRouteKind(route as Record<string, unknown>)
|
|
935
|
-
|
|
936
|
-
try {
|
|
937
|
-
if (kind === 'rpc') {
|
|
938
|
-
chunks = await emitRpcRoute(route as RPCHttpRouteDoc, ctx)
|
|
939
|
-
} else if (kind === 'api') {
|
|
940
|
-
chunks = await emitApiRoute(route as APIHttpRouteDoc, ctx)
|
|
941
|
-
} else if (kind === 'stream') {
|
|
942
|
-
chunks = await emitStreamRoute(route as StreamHttpRouteDoc, ctx)
|
|
943
|
-
} else if (kind === 'http-stream') {
|
|
944
|
-
chunks = await emitHttpStreamRoute(route as HttpStreamRouteDoc, ctx)
|
|
945
|
-
} else {
|
|
946
|
-
throw new Error(`Unknown route kind "${kind}"`)
|
|
947
|
-
}
|
|
948
|
-
} catch (err) {
|
|
949
|
-
const msg = err instanceof Error ? err.message : String(err)
|
|
950
|
-
throw new Error(
|
|
951
|
-
`[ts-procedures-codegen] Failed to emit route "${route.name}" (kind: ${kind}, scope: ${group.scopeKey}): ${msg}`
|
|
952
|
-
)
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
allTypeDeclarations.push(...chunks.typeDeclarations)
|
|
956
|
-
callables.push(chunks.callable)
|
|
957
|
-
if (chunks.hasStream) hasStream = true
|
|
958
|
-
if (chunks.hasErrors) scopeHasErrors = true
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// Build client import line — Result/ResultNoTyped are no longer referenced
|
|
962
|
-
// directly in scope files; the bindCallable helpers infer them from ClientInstance.
|
|
963
|
-
const clientImports = hasStream
|
|
964
|
-
? `import type { ClientInstance, ProcedureCallOptions, TypedStream } from '${clientImportPath}'`
|
|
965
|
-
: `import type { ClientInstance, ProcedureCallOptions } from '${clientImportPath}'`
|
|
966
|
-
|
|
967
|
-
// Build _errors import line when at least one route emits an Errors union.
|
|
968
|
-
// Namespace mode uses the qualified `${Service}Errors` namespace; flat mode
|
|
969
|
-
// pulls classes in via a wildcard alias (`_errors.UseCaseError`).
|
|
970
|
-
let errorsImport = ''
|
|
971
|
-
if (scopeHasErrors) {
|
|
972
|
-
if (namespaceTypes) {
|
|
973
|
-
errorsImport = `import type { ${toPascalCase(serviceName)}Errors } from './_errors'`
|
|
974
|
-
} else {
|
|
975
|
-
errorsImport = `import type * as _errors from './_errors'`
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
const callablesBlock = callables.join('\n\n')
|
|
980
|
-
|
|
981
|
-
// Assemble the type + callable section for this scope. ajsc already emitted
|
|
982
|
-
// bare model references (via `x-named-type`); `convertExtracted` / `convertBody`
|
|
983
|
-
// folded the reported names into `ctx.referencedModels`, which drives the
|
|
984
|
-
// `_models` import below. No post-pass over the body is needed.
|
|
985
|
-
let body: string
|
|
986
|
-
if (namespaceTypes) {
|
|
987
|
-
// Namespace mode: types AND `bindScope` live inside `export namespace ${pascal}`.
|
|
988
|
-
// Putting the function inside the namespace makes the merged symbol
|
|
989
|
-
// value+type, which lets `index.ts` use `export import ${pascal} = …`
|
|
990
|
-
// under `verbatimModuleSyntax: true`. (A type-only namespace would trip
|
|
991
|
-
// TS1269/TS1288.) Consumers call `${Pascal}.bindScope(client)`; the
|
|
992
|
-
// generated `index.ts` factory wires this internally.
|
|
993
|
-
const callableLines = indent(callablesBlock, ' ')
|
|
994
|
-
const namespaceMembers = [
|
|
995
|
-
...(allTypeDeclarations.length > 0 ? [allTypeDeclarations.join('\n\n')] : []),
|
|
996
|
-
[
|
|
997
|
-
' /** Binds every callable in this scope to a configured client. */',
|
|
998
|
-
' export function bindScope(client: ClientInstance) {',
|
|
999
|
-
' return {',
|
|
1000
|
-
callableLines,
|
|
1001
|
-
' }',
|
|
1002
|
-
' }',
|
|
1003
|
-
].join('\n'),
|
|
1004
|
-
].join('\n\n')
|
|
1005
|
-
|
|
1006
|
-
body = [
|
|
1007
|
-
`export namespace ${pascal} {`,
|
|
1008
|
-
namespaceMembers,
|
|
1009
|
-
'}',
|
|
1010
|
-
'',
|
|
1011
|
-
].join('\n')
|
|
1012
|
-
} else {
|
|
1013
|
-
// Flat mode: types at module level, `bind${pascal}Scope` standalone.
|
|
1014
|
-
const typesBlock = allTypeDeclarations.length > 0
|
|
1015
|
-
? allTypeDeclarations.join('\n') + '\n'
|
|
1016
|
-
: ''
|
|
1017
|
-
|
|
1018
|
-
body = [
|
|
1019
|
-
'// ── Types ────────────────────────────────────────',
|
|
1020
|
-
'',
|
|
1021
|
-
typesBlock,
|
|
1022
|
-
'// ── Callables ────────────────────────────────────',
|
|
1023
|
-
'',
|
|
1024
|
-
`export function bind${pascal}Scope(client: ClientInstance) {`,
|
|
1025
|
-
' return {',
|
|
1026
|
-
callablesBlock,
|
|
1027
|
-
' }',
|
|
1028
|
-
'}',
|
|
1029
|
-
'',
|
|
1030
|
-
].join('\n')
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
// Build the shared-models import line when any route referenced a `$id` model.
|
|
1034
|
-
// Matches the `_errors` import convention exactly: `'./_models'` with no `.js`.
|
|
1035
|
-
let modelsImport = ''
|
|
1036
|
-
if (referencedModels.size > 0) {
|
|
1037
|
-
const names = [...referencedModels].sort().join(', ')
|
|
1038
|
-
modelsImport = `import type { ${names} } from './_models'`
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
const importsBlock = [clientImports, errorsImport, modelsImport].filter(Boolean).join('\n')
|
|
1042
|
-
|
|
1043
|
-
return [
|
|
1044
|
-
CODEGEN_HEADER,
|
|
1045
|
-
importsBlock,
|
|
1046
|
-
'',
|
|
1047
|
-
body,
|
|
1048
|
-
].join('\n')
|
|
1049
|
-
}
|
|
2
|
+
* Thin re-export barrel for the TS scope emitter, which now lives in
|
|
3
|
+
* `./emit/`. Kept so existing imports of `./emit-scope.js` keep working.
|
|
4
|
+
*
|
|
5
|
+
* Module layout:
|
|
6
|
+
* - `emit/scope-file.ts` — `emitScopeFile` orchestration + assembly
|
|
7
|
+
* - `emit/rpc-route.ts` — `kind: 'rpc'` emitter
|
|
8
|
+
* - `emit/api-route.ts` — `kind: 'api'` emitter
|
|
9
|
+
* - `emit/stream-route.ts` — `kind: 'stream'` emitter
|
|
10
|
+
* - `emit/http-stream-route.ts` — `kind: 'http-stream'` emitter
|
|
11
|
+
* - `emit/format-types.ts` — schema→type conversion + formatTypes helpers
|
|
12
|
+
* - `emit/declarations.ts` — `indent` + `DeclarationCollector`
|
|
13
|
+
* - `emit/route-shared.ts` — errors-union / JSDoc / naming helpers
|
|
14
|
+
* - `emit/context.ts` — shared `EmitRouteContext` / `RouteChunks`
|
|
15
|
+
*/
|
|
16
|
+
export { emitScopeFile, type EmitScopeOptions } from './emit/scope-file.js'
|