ts-procedures 8.6.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-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/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 +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 +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/__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-scope.test.ts +2 -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} +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/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,7 +1,6 @@
|
|
|
1
|
-
|
|
2
1
|
import { describe, expect, it, test } from 'vitest'
|
|
3
|
-
import { Procedures } from './
|
|
4
|
-
import {
|
|
2
|
+
import { Procedures } from './procedures.js'
|
|
3
|
+
import { ProcedureRegistrationError } from './errors.js'
|
|
5
4
|
import { Type } from 'typebox'
|
|
6
5
|
|
|
7
6
|
describe('Procedures', () => {
|
|
@@ -26,8 +25,8 @@ describe('Procedures', () => {
|
|
|
26
25
|
'test-docs',
|
|
27
26
|
{
|
|
28
27
|
schema: {
|
|
29
|
-
params:
|
|
30
|
-
returnType:
|
|
28
|
+
params: Type.Object({ name: Type.String() }),
|
|
29
|
+
returnType: Type.String(),
|
|
31
30
|
},
|
|
32
31
|
},
|
|
33
32
|
async () => {
|
|
@@ -62,6 +61,9 @@ describe('Procedures', () => {
|
|
|
62
61
|
expect(() => {
|
|
63
62
|
Create('DuplicateTest', {}, async () => 'second')
|
|
64
63
|
}).toThrow('Procedure with name DuplicateTest is already registered')
|
|
64
|
+
expect(() => {
|
|
65
|
+
Create('DuplicateTest', {}, async () => 'second')
|
|
66
|
+
}).toThrow(ProcedureRegistrationError)
|
|
65
67
|
})
|
|
66
68
|
|
|
67
69
|
test('Procedures - getProcedure returns specific procedure', () => {
|
|
@@ -122,11 +124,11 @@ describe('Procedures', () => {
|
|
|
122
124
|
})
|
|
123
125
|
})
|
|
124
126
|
|
|
125
|
-
describe('
|
|
126
|
-
test('
|
|
127
|
+
describe('Procedures({ validation: false })', () => {
|
|
128
|
+
test('validation: false does not interfere with onCreate callback', async () => {
|
|
127
129
|
const seen: string[] = []
|
|
128
130
|
const { Create } = Procedures({
|
|
129
|
-
|
|
131
|
+
validation: false,
|
|
130
132
|
onCreate: (proc) => {
|
|
131
133
|
seen.push(proc.name)
|
|
132
134
|
},
|
|
@@ -141,9 +143,9 @@ describe('builder.config.noRuntimeValidation', () => {
|
|
|
141
143
|
expect(seen).toEqual(['Registered'])
|
|
142
144
|
})
|
|
143
145
|
|
|
144
|
-
test('
|
|
145
|
-
// @ts-expect-error - only `
|
|
146
|
-
void Procedures({
|
|
146
|
+
test('validation: true is rejected by the type system', () => {
|
|
147
|
+
// @ts-expect-error - only `false` or `{ ajv }` (or omitted) is allowed for validation
|
|
148
|
+
void Procedures({ validation: true })
|
|
147
149
|
})
|
|
148
150
|
})
|
|
149
151
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { makeCreate } from './create.js'
|
|
2
|
+
import { makeCreateStream } from './create-stream.js'
|
|
3
|
+
import { makeCreateHttp } from './create-http.js'
|
|
4
|
+
import { makeCreateHttpStream } from './create-http-stream.js'
|
|
5
|
+
import { createValidatorCompiler } from '../schema/compile.js'
|
|
6
|
+
import { typeboxAdapter } from '../schema/typebox.js'
|
|
7
|
+
import type { FactoryRuntime } from './internal.js'
|
|
8
|
+
import type { AnyProcedureRegistration, ProceduresOptions, TNoContextProvided } from './types.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a procedure factory.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* type Ctx = { db: Db }
|
|
15
|
+
* const { Create, CreateStream, CreateHttp, CreateHttpStream, getProcedures } =
|
|
16
|
+
* Procedures<Ctx>({
|
|
17
|
+
* validation: { ajv: { coerceTypes: false } }, // or `false` to skip per-call validation
|
|
18
|
+
* schema: { adapters: [myZodAdapter] }, // TypeBox is built in
|
|
19
|
+
* http: { pathPrefix: '/v1', scope: 'billing' }, // CreateHttp* defaults
|
|
20
|
+
* onCreate: (procedure) => log(procedure.kind, procedure.name),
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* - `TContext` — the context every handler receives (plus injected `error()`
|
|
25
|
+
* and `signal`).
|
|
26
|
+
* - `TExtendedConfig` — extra per-procedure config fields (e.g. `scope`,
|
|
27
|
+
* `version`, auth flags) carried through to registrations, `info`, and
|
|
28
|
+
* `onCreate`.
|
|
29
|
+
*/
|
|
30
|
+
export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unknown>(
|
|
31
|
+
options?: ProceduresOptions<TContext, TExtendedConfig>,
|
|
32
|
+
) {
|
|
33
|
+
const registry = new Map<string, AnyProcedureRegistration<TContext, TExtendedConfig>>()
|
|
34
|
+
|
|
35
|
+
const runtime: FactoryRuntime<TContext, TExtendedConfig> = {
|
|
36
|
+
registry,
|
|
37
|
+
onCreate: options?.onCreate,
|
|
38
|
+
skipValidation: options?.validation === false,
|
|
39
|
+
adapters: [...(options?.schema?.adapters ?? []), typeboxAdapter],
|
|
40
|
+
compile: createValidatorCompiler(options?.validation === false ? undefined : options?.validation),
|
|
41
|
+
httpDefaults: options?.http,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const Create = makeCreate<TContext, TExtendedConfig>(runtime)
|
|
45
|
+
const CreateStream = makeCreateStream<TContext, TExtendedConfig>(runtime)
|
|
46
|
+
const CreateHttp = makeCreateHttp<TContext>(runtime)
|
|
47
|
+
const CreateHttpStream = makeCreateHttpStream<TContext>(runtime)
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
/** All registered procedures, in registration order. */
|
|
51
|
+
getProcedures: () => {
|
|
52
|
+
return Array.from(registry.values())
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/** A specific procedure by name. */
|
|
56
|
+
getProcedure: (name: string) => {
|
|
57
|
+
return registry.get(name)
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/** Removes a procedure by name. Returns whether it existed. */
|
|
61
|
+
removeProcedure: (name: string) => {
|
|
62
|
+
return registry.delete(name)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/** Clears all registered procedures. */
|
|
66
|
+
clear: () => {
|
|
67
|
+
registry.clear()
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
Create,
|
|
71
|
+
CreateStream,
|
|
72
|
+
CreateHttp,
|
|
73
|
+
CreateHttpStream,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type * as AJV from 'ajv'
|
|
2
|
+
import type { ProcedureError } from './errors.js'
|
|
3
|
+
import type { SchemaAdapter } from '../schema/adapter.js'
|
|
4
|
+
import type { TJSONSchema } from '../schema/json-schema.js'
|
|
5
|
+
import type { Validate } from '../schema/compile.js'
|
|
6
|
+
|
|
7
|
+
export type TNoContextProvided = unknown
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Context the framework injects into every handler on top of the
|
|
11
|
+
* factory-level `TContext`.
|
|
12
|
+
*/
|
|
13
|
+
export type TLocalContext = {
|
|
14
|
+
/** Creates a `ProcedureError` carrying this procedure's name + definition site. */
|
|
15
|
+
error: (message: string, meta?: object) => ProcedureError
|
|
16
|
+
/** Present when the server implementation provides one (HonoAppBuilder does). */
|
|
17
|
+
signal?: AbortSignal
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Stream handlers always get a signal (aborts on client disconnect or completion). */
|
|
21
|
+
export type TStreamContext = TLocalContext & {
|
|
22
|
+
signal: AbortSignal
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Discriminant on every procedure registration that drives builder routing.
|
|
27
|
+
*
|
|
28
|
+
* - `'rpc'` — Create() registration
|
|
29
|
+
* - `'rpc-stream'` — CreateStream() registration
|
|
30
|
+
* - `'http'` — CreateHttp() registration
|
|
31
|
+
* - `'http-stream'` — CreateHttpStream() registration
|
|
32
|
+
*/
|
|
33
|
+
export type ProcedureKind = 'rpc' | 'rpc-stream' | 'http' | 'http-stream'
|
|
34
|
+
|
|
35
|
+
export type TProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
|
|
36
|
+
name: string
|
|
37
|
+
kind: 'rpc'
|
|
38
|
+
config: {
|
|
39
|
+
description?: string
|
|
40
|
+
schema?: {
|
|
41
|
+
params?: TJSONSchema
|
|
42
|
+
returnType?: TJSONSchema
|
|
43
|
+
}
|
|
44
|
+
validation?: {
|
|
45
|
+
params?: Validate
|
|
46
|
+
}
|
|
47
|
+
} & TExtendedConfig
|
|
48
|
+
handler: (ctx: TContext, params?: any) => Promise<any>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
|
|
52
|
+
name: string
|
|
53
|
+
kind: 'rpc-stream'
|
|
54
|
+
config: {
|
|
55
|
+
description?: string
|
|
56
|
+
schema?: {
|
|
57
|
+
params?: TJSONSchema
|
|
58
|
+
yieldType?: TJSONSchema
|
|
59
|
+
returnType?: TJSONSchema
|
|
60
|
+
}
|
|
61
|
+
validation?: {
|
|
62
|
+
params?: Validate
|
|
63
|
+
yield?: Validate
|
|
64
|
+
}
|
|
65
|
+
validateYields?: boolean
|
|
66
|
+
} & TExtendedConfig
|
|
67
|
+
handler: (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
|
|
71
|
+
|
|
72
|
+
export type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
|
|
73
|
+
path: string
|
|
74
|
+
method: HttpMethod
|
|
75
|
+
successStatus?: number
|
|
76
|
+
scope?: string
|
|
77
|
+
errors?: TErrorKey[]
|
|
78
|
+
description?: string
|
|
79
|
+
schema: {
|
|
80
|
+
req?: TReq
|
|
81
|
+
res?: TRes
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type THttpProcedureRegistration<TContext = unknown> = {
|
|
86
|
+
name: string
|
|
87
|
+
kind: 'http'
|
|
88
|
+
config: {
|
|
89
|
+
path: string
|
|
90
|
+
method: HttpMethod
|
|
91
|
+
successStatus?: number
|
|
92
|
+
scope?: string
|
|
93
|
+
errors?: string[]
|
|
94
|
+
description?: string
|
|
95
|
+
schema?: {
|
|
96
|
+
req?: Record<string, TJSONSchema>
|
|
97
|
+
res?: { body?: TJSONSchema; headers?: TJSONSchema }
|
|
98
|
+
}
|
|
99
|
+
validation?: {
|
|
100
|
+
req?: Record<string, Validate>
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
handler: (ctx: TContext, req?: any) => Promise<any>
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type THttpStreamProcedureRegistration<TContext = unknown> = {
|
|
107
|
+
name: string
|
|
108
|
+
kind: 'http-stream'
|
|
109
|
+
config: {
|
|
110
|
+
path: string
|
|
111
|
+
method: HttpMethod
|
|
112
|
+
scope?: string
|
|
113
|
+
errors?: string[]
|
|
114
|
+
description?: string
|
|
115
|
+
schema?: {
|
|
116
|
+
req?: Record<string, TJSONSchema>
|
|
117
|
+
res?: { headers?: TJSONSchema }
|
|
118
|
+
yield?: TJSONSchema
|
|
119
|
+
returnType?: TJSONSchema
|
|
120
|
+
}
|
|
121
|
+
validation?: {
|
|
122
|
+
req?: Record<string, Validate>
|
|
123
|
+
yield?: Validate
|
|
124
|
+
}
|
|
125
|
+
validateYields?: boolean
|
|
126
|
+
}
|
|
127
|
+
handler: (
|
|
128
|
+
ctx: TContext,
|
|
129
|
+
req?: any,
|
|
130
|
+
) => Promise<{ stream: AsyncGenerator<any, any, unknown>; initialHeaders?: Record<string, string> }>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The shape every creator returns: the handler under its own name (so
|
|
135
|
+
* `const { GetUser } = Create('GetUser', ...)` works), the same handler as
|
|
136
|
+
* `procedure`, and the introspectable `info`.
|
|
137
|
+
*/
|
|
138
|
+
export type ProcedureResult<TName extends string, THandler, TInfo> = {
|
|
139
|
+
[K in TName]: THandler
|
|
140
|
+
} & {
|
|
141
|
+
procedure: THandler
|
|
142
|
+
info: TInfo
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Union of all four registration shapes a factory can hold. */
|
|
146
|
+
export type AnyProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> =
|
|
147
|
+
| TProcedureRegistration<TContext, TExtendedConfig>
|
|
148
|
+
| TStreamProcedureRegistration<TContext, TExtendedConfig>
|
|
149
|
+
| THttpProcedureRegistration<TContext>
|
|
150
|
+
| THttpStreamProcedureRegistration<TContext>
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Options for `Procedures()`.
|
|
154
|
+
*/
|
|
155
|
+
export type ProceduresOptions<TContext = unknown, TExtendedConfig = unknown> = {
|
|
156
|
+
/**
|
|
157
|
+
* Called once per registered procedure with the full registration object.
|
|
158
|
+
* Use the `kind` discriminant to narrow to the specific shape. This is the
|
|
159
|
+
* seam for custom framework integration (Fastify, Koa, raw http, ...).
|
|
160
|
+
*/
|
|
161
|
+
onCreate?: (procedure: AnyProcedureRegistration<TContext, TExtendedConfig>) => void
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Runtime validation behavior:
|
|
165
|
+
* - omitted — validate every call with the default AJV configuration
|
|
166
|
+
* (`allErrors`, `coerceTypes`, `removeAdditional`)
|
|
167
|
+
* - `false` — skip per-call validation for the whole factory. JSON Schema
|
|
168
|
+
* and validators are still computed at registration time (bad schemas
|
|
169
|
+
* still fail fast); only the per-call runs are skipped. For trusted
|
|
170
|
+
* internal factories whose callers are type-checked at build time.
|
|
171
|
+
* - `{ ajv }` — AJV options merged over the defaults, or a configured
|
|
172
|
+
* `Ajv` instance used as-is.
|
|
173
|
+
*/
|
|
174
|
+
validation?: false | { ajv?: AJV.Ajv | AJV.Options }
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Schema-library integration. TypeBox is built in; prepend adapters here to
|
|
178
|
+
* register procedures with other schema libraries (see `SchemaAdapter`).
|
|
179
|
+
*/
|
|
180
|
+
schema?: {
|
|
181
|
+
adapters?: SchemaAdapter[]
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Factory-level defaults for `CreateHttp` / `CreateHttpStream` routes:
|
|
186
|
+
* - `pathPrefix` — prepended to every route's `path` at registration time
|
|
187
|
+
* (this is part of the route's identity, unlike a server adapter's mount
|
|
188
|
+
* prefix)
|
|
189
|
+
* - `scope` — default codegen scope for routes that don't set one
|
|
190
|
+
*/
|
|
191
|
+
http?: {
|
|
192
|
+
pathPrefix?: string
|
|
193
|
+
scope?: string
|
|
194
|
+
}
|
|
195
|
+
}
|
package/src/exports.ts
CHANGED
|
@@ -1,11 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/**
|
|
2
|
+
* ts-procedures — root entry point.
|
|
3
|
+
*
|
|
4
|
+
* Subpath exports:
|
|
5
|
+
* - `ts-procedures/hono` — HonoAppBuilder server adapter
|
|
6
|
+
* - `ts-procedures/astro` — Astro catch-all adapter (delegates to Hono apps)
|
|
7
|
+
* - `ts-procedures/server` — transport-agnostic server layer (build your own adapter)
|
|
8
|
+
* - `ts-procedures/http` — HTTP doc/config types (types only)
|
|
9
|
+
* - `ts-procedures/http-docs` — DocRegistry (multi-app envelope aggregation)
|
|
10
|
+
* - `ts-procedures/http-errors` — error taxonomy helpers
|
|
11
|
+
* - `ts-procedures/client` — runtime client for generated code
|
|
12
|
+
* - `ts-procedures/codegen` — client code generation (also: npx ts-procedures-codegen)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Core — the Procedures factory and everything handlers touch
|
|
16
|
+
export { Procedures } from './core/procedures.js'
|
|
17
|
+
export type {
|
|
18
|
+
ProceduresOptions,
|
|
19
|
+
ProcedureKind,
|
|
20
|
+
AnyProcedureRegistration,
|
|
21
|
+
TProcedureRegistration,
|
|
22
|
+
TStreamProcedureRegistration,
|
|
23
|
+
THttpProcedureRegistration,
|
|
24
|
+
THttpStreamProcedureRegistration,
|
|
25
|
+
TCreateHttpConfig,
|
|
26
|
+
TLocalContext,
|
|
27
|
+
TStreamContext,
|
|
28
|
+
TNoContextProvided,
|
|
29
|
+
HttpMethod,
|
|
30
|
+
} from './core/types.js'
|
|
31
|
+
export type { HttpReturn } from './core/create-http.js'
|
|
32
|
+
export {
|
|
33
|
+
ProcedureError,
|
|
34
|
+
ProcedureValidationError,
|
|
35
|
+
ProcedureRegistrationError,
|
|
36
|
+
ProcedureYieldValidationError,
|
|
37
|
+
} from './core/errors.js'
|
|
38
|
+
export type { ProcedureErrorKind } from './core/errors.js'
|
|
39
|
+
export { captureDefinitionInfo, formatDefinitionInfo } from './core/definition-site.js'
|
|
40
|
+
export type { DefinitionInfo, DefinitionLocation } from './core/definition-site.js'
|
|
41
|
+
|
|
42
|
+
// Schema — adapters, inference, validation
|
|
43
|
+
export type { SchemaAdapter } from './schema/adapter.js'
|
|
44
|
+
export { extractJsonSchema } from './schema/adapter.js'
|
|
45
|
+
export { typeboxAdapter, isTypeboxSchema } from './schema/typebox.js'
|
|
46
|
+
export { computeSchema } from './schema/compute-schema.js'
|
|
47
|
+
export type { ComputedSchema } from './schema/compute-schema.js'
|
|
48
|
+
export { createValidatorCompiler, DEFAULT_AJV_OPTIONS } from './schema/compile.js'
|
|
49
|
+
export type {
|
|
50
|
+
Validate,
|
|
51
|
+
ValidatorCompiler,
|
|
52
|
+
ValidationOptions,
|
|
53
|
+
TSchemaValidationError,
|
|
54
|
+
} from './schema/compile.js'
|
|
55
|
+
export type { Infer, TSchemaLib, TJSONSchema, Prettify } from './schema/json-schema.js'
|
|
56
|
+
|
|
57
|
+
// Doc envelope — offline codegen input
|
|
58
|
+
export { writeDocEnvelope } from './server/doc-envelope.js'
|
|
59
|
+
export type { DocEnvelopeSource } from './server/doc-envelope.js'
|
|
60
|
+
export type { DocEnvelope } from './server/types.js'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { Type } from 'typebox'
|
|
3
|
+
import { extractJsonSchema } from './adapter.js'
|
|
4
|
+
import type { SchemaAdapter } from './adapter.js'
|
|
5
|
+
import { typeboxAdapter } from './typebox.js'
|
|
6
|
+
|
|
7
|
+
describe('extractJsonSchema()', () => {
|
|
8
|
+
const typebox = Type.Object({ name: Type.String() })
|
|
9
|
+
|
|
10
|
+
test('it extracts TypeBox json-schema', () => {
|
|
11
|
+
expect(extractJsonSchema(typebox, [typeboxAdapter])).toMatchObject({
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: { name: { type: 'string' } },
|
|
14
|
+
required: ['name'],
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('it returns undefined when no adapter recognizes the value', () => {
|
|
19
|
+
expect(extractJsonSchema({ type: 'object' }, [typeboxAdapter])).toBeUndefined()
|
|
20
|
+
expect(extractJsonSchema('string value', [typeboxAdapter])).toBeUndefined()
|
|
21
|
+
expect(extractJsonSchema(null, [typeboxAdapter])).toBeUndefined()
|
|
22
|
+
expect(extractJsonSchema(undefined, [typeboxAdapter])).toBeUndefined()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('it returns undefined when the adapter list is empty', () => {
|
|
26
|
+
expect(extractJsonSchema(typebox, [])).toBeUndefined()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('it runs the adapter chain in order — first match wins', () => {
|
|
30
|
+
const everything: SchemaAdapter = {
|
|
31
|
+
name: 'everything',
|
|
32
|
+
detect: () => true,
|
|
33
|
+
toJsonSchema: () => ({ type: 'string' }),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// typeboxAdapter first: TypeBox schema goes through identity conversion
|
|
37
|
+
expect(extractJsonSchema(typebox, [typeboxAdapter, everything])).toBe(typebox)
|
|
38
|
+
// everything first: it claims the value before typeboxAdapter sees it
|
|
39
|
+
expect(extractJsonSchema(typebox, [everything, typeboxAdapter])).toEqual({ type: 'string' })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('it supports custom adapters for non-TypeBox schema objects', () => {
|
|
43
|
+
class FakeZodType {
|
|
44
|
+
constructor(readonly jsonSchema: Record<string, any>) {}
|
|
45
|
+
}
|
|
46
|
+
const fakeZodAdapter: SchemaAdapter = {
|
|
47
|
+
name: 'fake-zod',
|
|
48
|
+
detect: (s) => s instanceof FakeZodType,
|
|
49
|
+
toJsonSchema: (s) => (s as FakeZodType).jsonSchema,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const schema = new FakeZodType({ type: 'object', properties: { id: { type: 'number' } } })
|
|
53
|
+
expect(extractJsonSchema(schema, [typeboxAdapter, fakeZodAdapter])).toEqual({
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: { id: { type: 'number' } },
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TJSONSchema } from './json-schema.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bridges a schema library to the framework.
|
|
5
|
+
*
|
|
6
|
+
* The framework only ever needs one thing from a schema object: its JSON
|
|
7
|
+
* Schema. Everything downstream (AJV validation, route docs, codegen) works on
|
|
8
|
+
* JSON Schema. Implement this interface to plug in any schema library:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const zodAdapter: SchemaAdapter = {
|
|
12
|
+
* name: 'zod',
|
|
13
|
+
* detect: (s) => s instanceof z.ZodType,
|
|
14
|
+
* toJsonSchema: (s) => z.toJSONSchema(s as z.ZodType),
|
|
15
|
+
* }
|
|
16
|
+
* const { Create } = Procedures({ schema: { adapters: [zodAdapter] } })
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Note: compile-time type inference (`Infer<T>`) is only built in for TypeBox;
|
|
20
|
+
* custom adapters get runtime validation and docs, with params inferred as
|
|
21
|
+
* `unknown` unless you annotate handler types yourself.
|
|
22
|
+
*/
|
|
23
|
+
export interface SchemaAdapter {
|
|
24
|
+
/** Short identifier used in registration error messages. */
|
|
25
|
+
name: string
|
|
26
|
+
/** Whether this adapter recognizes the given schema object. */
|
|
27
|
+
detect(schema: unknown): boolean
|
|
28
|
+
/** Converts a recognized schema object to JSON Schema. */
|
|
29
|
+
toJsonSchema(schema: unknown): TJSONSchema
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Runs the adapter chain against a schema value. Returns the JSON Schema from
|
|
34
|
+
* the first adapter whose `detect` matches, or `undefined` when no adapter
|
|
35
|
+
* recognizes the value (the caller reports the registration error).
|
|
36
|
+
*/
|
|
37
|
+
export function extractJsonSchema(
|
|
38
|
+
schema: unknown,
|
|
39
|
+
adapters: readonly SchemaAdapter[],
|
|
40
|
+
): TJSONSchema | undefined {
|
|
41
|
+
for (const adapter of adapters) {
|
|
42
|
+
if (adapter.detect(schema)) return adapter.toJsonSchema(schema)
|
|
43
|
+
}
|
|
44
|
+
return undefined
|
|
45
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { Type } from 'typebox'
|
|
3
|
+
import * as AJV from 'ajv'
|
|
4
|
+
import { createValidatorCompiler, DEFAULT_AJV_OPTIONS } from './compile.js'
|
|
5
|
+
|
|
6
|
+
describe('createValidatorCompiler', () => {
|
|
7
|
+
describe('default configuration', () => {
|
|
8
|
+
const compile = createValidatorCompiler()
|
|
9
|
+
|
|
10
|
+
test('validator returns {} on success', () => {
|
|
11
|
+
const validate = compile(Type.Object({ name: Type.String(), age: Type.Number() }))
|
|
12
|
+
const result = validate({ name: 'John', age: 30 })
|
|
13
|
+
expect(result).toEqual({})
|
|
14
|
+
expect(result.errors).toBeUndefined()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('validator returns { errors } on failure', () => {
|
|
18
|
+
const validate = compile(Type.Object({ name: Type.String() }))
|
|
19
|
+
const result = validate({})
|
|
20
|
+
expect(result.errors).toBeDefined()
|
|
21
|
+
expect(result.errors?.[0]?.message).toMatch(/must have required property 'name'/)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('allErrors: reports every failure, not just the first', () => {
|
|
25
|
+
const validate = compile(
|
|
26
|
+
Type.Object({ name: Type.String(), age: Type.Number() }),
|
|
27
|
+
)
|
|
28
|
+
const result = validate({ name: { nested: '' }, age: 'not-a-number' })
|
|
29
|
+
expect(result.errors?.length).toEqual(2)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('coerceTypes: coerces primitive types in place', () => {
|
|
33
|
+
const validate = compile(Type.Object({ age: Type.Number() }))
|
|
34
|
+
const value: Record<string, unknown> = { age: '30' }
|
|
35
|
+
expect(validate(value).errors).toBeUndefined()
|
|
36
|
+
expect(value.age).toBe(30)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('removeAdditional: strips unknown properties in place', () => {
|
|
40
|
+
const validate = compile(
|
|
41
|
+
Type.Object({ name: Type.String() }, { additionalProperties: false }),
|
|
42
|
+
)
|
|
43
|
+
const value: Record<string, unknown> = { name: 'John', extra: 'gone' }
|
|
44
|
+
expect(validate(value).errors).toBeUndefined()
|
|
45
|
+
expect(value).toEqual({ name: 'John' })
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('ajv-formats is wired in (format keywords validate)', () => {
|
|
49
|
+
const validate = compile(Type.Object({ email: Type.String({ format: 'email' }) }))
|
|
50
|
+
expect(validate({ email: 'john@example.com' }).errors).toBeUndefined()
|
|
51
|
+
expect(validate({ email: 'not-an-email' }).errors).toBeDefined()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('compile throws on an invalid JSON schema', () => {
|
|
55
|
+
expect(() => compile({ type: 'invalid-schema-type' })).toThrow()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('DEFAULT_AJV_OPTIONS matches the documented v8 behavior', () => {
|
|
59
|
+
expect(DEFAULT_AJV_OPTIONS).toEqual({
|
|
60
|
+
allErrors: true,
|
|
61
|
+
coerceTypes: true,
|
|
62
|
+
removeAdditional: true,
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('custom AJV options merge over the defaults', () => {
|
|
68
|
+
test('coerceTypes: false disables coercion while keeping other defaults', () => {
|
|
69
|
+
const compile = createValidatorCompiler({ ajv: { coerceTypes: false } })
|
|
70
|
+
const validate = compile(Type.Object({ age: Type.Number(), name: Type.String() }))
|
|
71
|
+
|
|
72
|
+
// No coercion: a string is no longer accepted for a number
|
|
73
|
+
expect(validate({ age: '30', name: 'John' }).errors).toBeDefined()
|
|
74
|
+
expect(validate({ age: 30, name: 'John' }).errors).toBeUndefined()
|
|
75
|
+
|
|
76
|
+
// allErrors default still applies (both bad fields reported)
|
|
77
|
+
expect(validate({ age: 'x', name: 42 }).errors?.length).toEqual(2)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
describe('preconfigured Ajv instance', () => {
|
|
82
|
+
test('uses the instance as-is (no default options applied)', () => {
|
|
83
|
+
const ajv = new AJV.Ajv() // no coerceTypes, no allErrors, no formats
|
|
84
|
+
const compile = createValidatorCompiler({ ajv })
|
|
85
|
+
const validate = compile(Type.Object({ age: Type.Number(), name: Type.String() }))
|
|
86
|
+
|
|
87
|
+
// No coercion from the default config
|
|
88
|
+
expect(validate({ age: '30', name: 'John' }).errors).toBeDefined()
|
|
89
|
+
// No allErrors: only the first failure is reported
|
|
90
|
+
expect(validate({ age: 'x', name: 42 }).errors?.length).toEqual(1)
|
|
91
|
+
// Valid input still passes
|
|
92
|
+
expect(validate({ age: 30, name: 'John' }).errors).toBeUndefined()
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { default as addFormats } from 'ajv-formats'
|
|
2
|
+
import * as AJV from 'ajv'
|
|
3
|
+
import type { TJSONSchema } from './json-schema.js'
|
|
4
|
+
|
|
5
|
+
export type TSchemaValidationError = AJV.ErrorObject
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A compiled validator. Returns `{}` on success, `{ errors }` on failure —
|
|
9
|
+
* the same shape AJV-based validators have carried since v1, and the shape
|
|
10
|
+
* every registration's `config.validation.*` function exposes.
|
|
11
|
+
*/
|
|
12
|
+
export type Validate = (value?: any) => { errors?: TSchemaValidationError[] }
|
|
13
|
+
|
|
14
|
+
/** Compiles a JSON Schema into a {@link Validate} function. */
|
|
15
|
+
export type ValidatorCompiler = (jsonSchema: TJSONSchema) => Validate
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `Procedures({ validation })` shape:
|
|
19
|
+
* - omitted — validate with the default AJV configuration
|
|
20
|
+
* - `false` — skip per-call validation entirely (schemas/validators are still
|
|
21
|
+
* computed at registration time so bad schemas fail fast)
|
|
22
|
+
* - `{ ajv }` — custom AJV options merged over the defaults, or a fully
|
|
23
|
+
* configured `Ajv` instance to use as-is
|
|
24
|
+
*/
|
|
25
|
+
export type ValidationOptions = false | { ajv?: AJV.Ajv | AJV.Options }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Default AJV configuration — unchanged from every prior major version:
|
|
29
|
+
* report all errors, coerce primitive types, strip unknown properties.
|
|
30
|
+
*/
|
|
31
|
+
export const DEFAULT_AJV_OPTIONS: AJV.Options = {
|
|
32
|
+
allErrors: true,
|
|
33
|
+
coerceTypes: true,
|
|
34
|
+
removeAdditional: true,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isAjvInstance(value: AJV.Ajv | AJV.Options | undefined): value is AJV.Ajv {
|
|
38
|
+
return typeof (value as { compile?: unknown } | undefined)?.compile === 'function'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Creates a {@link ValidatorCompiler} backed by AJV. Each `Procedures()`
|
|
43
|
+
* factory owns one compiler, so AJV configuration is per-factory instead of
|
|
44
|
+
* a module-level singleton.
|
|
45
|
+
*/
|
|
46
|
+
export function createValidatorCompiler(options?: { ajv?: AJV.Ajv | AJV.Options }): ValidatorCompiler {
|
|
47
|
+
const ajv = isAjvInstance(options?.ajv)
|
|
48
|
+
? options.ajv
|
|
49
|
+
: // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
50
|
+
// @ts-expect-error ajv-formats' types lag behind ajv's
|
|
51
|
+
(addFormats(new AJV.Ajv({ ...DEFAULT_AJV_OPTIONS, ...options?.ajv })) as AJV.Ajv)
|
|
52
|
+
|
|
53
|
+
return function compile(jsonSchema: TJSONSchema): Validate {
|
|
54
|
+
const validator = ajv.compile(jsonSchema)
|
|
55
|
+
return (value?: any) => {
|
|
56
|
+
const valid = validator(value)
|
|
57
|
+
if (!valid) {
|
|
58
|
+
const errors = validator.errors
|
|
59
|
+
return { errors: errors?.length ? [...errors] : undefined }
|
|
60
|
+
}
|
|
61
|
+
return {}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|