@lovrozagar/honey 0.6.8 → 0.7.1
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 +514 -192
- package/bin/honey.js +25 -0
- package/dist/accepts.d.ts +7 -1
- package/dist/accepts.d.ts.map +1 -1
- package/dist/accepts.js +126 -0
- package/dist/accepts.js.map +1 -0
- package/dist/body-kind.d.ts +26 -0
- package/dist/body-kind.d.ts.map +1 -0
- package/dist/body-kind.js +70 -0
- package/dist/body-kind.js.map +1 -0
- package/dist/body-limit.d.ts +5 -1
- package/dist/body-limit.d.ts.map +1 -1
- package/dist/body-limit.js +126 -0
- package/dist/body-limit.js.map +1 -0
- package/dist/build/index.d.ts +19 -3
- package/dist/build/index.d.ts.map +1 -1
- package/dist/build/index.js +173 -0
- package/dist/build/index.js.map +1 -0
- package/dist/cli-args.d.ts +33 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +102 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-go/auth.go +26 -0
- package/dist/cli-go/config.go +192 -0
- package/dist/cli-go/errors.go +75 -0
- package/dist/cli-go/multipart.go +79 -0
- package/dist/cli-go/output.go +163 -0
- package/dist/cli-go/root.go +30 -0
- package/dist/cli-go/stream.go +88 -0
- package/dist/cli-go/version.go +5 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +209 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/defaults.js +48 -0
- package/dist/client/defaults.js.map +1 -0
- package/dist/client/error.d.ts +35 -0
- package/dist/client/error.d.ts.map +1 -1
- package/dist/client/error.js +71 -0
- package/dist/client/error.js.map +1 -0
- package/dist/client/http.d.ts +67 -8
- package/dist/client/http.d.ts.map +1 -1
- package/dist/client/http.js +571 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +95 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/path.d.ts +26 -0
- package/dist/client/path.d.ts.map +1 -0
- package/dist/client/path.js +163 -0
- package/dist/client/path.js.map +1 -0
- package/dist/client/redirect-policy.d.ts +12 -0
- package/dist/client/redirect-policy.d.ts.map +1 -0
- package/dist/client/redirect-policy.js +20 -0
- package/dist/client/redirect-policy.js.map +1 -0
- package/dist/client/sdk.d.ts +36 -0
- package/dist/client/sdk.d.ts.map +1 -1
- package/dist/client/sdk.js +292 -0
- package/dist/client/sdk.js.map +1 -0
- package/dist/client/sse.d.ts +13 -0
- package/dist/client/sse.d.ts.map +1 -1
- package/dist/client/sse.js +129 -0
- package/dist/client/sse.js.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/ws.d.ts +9 -2
- package/dist/client/ws.d.ts.map +1 -1
- package/dist/client/ws.js +112 -0
- package/dist/client/ws.js.map +1 -0
- package/dist/client-go/errors.go +236 -0
- package/dist/client-go/export.go +44 -0
- package/dist/client-go/invalidation.go +371 -0
- package/dist/client-go/nullable.go +62 -0
- package/dist/client-go/realtime.go +354 -0
- package/dist/client-go/result.go +20 -0
- package/dist/client-go/runtime.go +975 -0
- package/dist/client-go/sse.go +159 -0
- package/dist/client-go/transport.go +495 -0
- package/dist/client-go/ws.go +128 -0
- package/dist/client-info.d.ts +36 -0
- package/dist/client-info.d.ts.map +1 -0
- package/dist/client-info.js +53 -0
- package/dist/client-info.js.map +1 -0
- package/dist/client-mcp/README.md +10 -0
- package/dist/client-mcp/server.ts +221 -0
- package/dist/client-mcp/types.ts +19 -0
- package/dist/client-python/_errors.py +115 -0
- package/dist/client-python/_invalidation.py +494 -0
- package/dist/client-python/_realtime.py +179 -0
- package/dist/client-python/_runtime.py +860 -0
- package/dist/client-python/_sse.py +155 -0
- package/dist/client-python/_transport.py +366 -0
- package/dist/client-python/_ws.py +159 -0
- package/dist/client-python/py.typed +0 -0
- package/dist/client-rust/errors.rs +220 -0
- package/dist/client-rust/invalidation.rs +412 -0
- package/dist/client-rust/invalidation_sync.rs +93 -0
- package/dist/client-rust/nullable.rs +17 -0
- package/dist/client-rust/realtime.rs +702 -0
- package/dist/client-rust/result.rs +22 -0
- package/dist/client-rust/runtime.rs +980 -0
- package/dist/client-rust/runtime_sync.rs +484 -0
- package/dist/client-rust/sse.rs +188 -0
- package/dist/client-rust/ws.rs +167 -0
- package/dist/codegen-go-cli.d.ts.map +1 -1
- package/dist/codegen-go-cli.js +964 -0
- package/dist/codegen-go-cli.js.map +1 -0
- package/dist/codegen-go.d.ts +15 -3
- package/dist/codegen-go.d.ts.map +1 -1
- package/dist/codegen-go.js +872 -0
- package/dist/codegen-go.js.map +1 -0
- package/dist/codegen-hash.js +9 -0
- package/dist/codegen-hash.js.map +1 -0
- package/dist/codegen-ir.d.ts +69 -2
- package/dist/codegen-ir.d.ts.map +1 -1
- package/dist/codegen-ir.js +661 -0
- package/dist/codegen-ir.js.map +1 -0
- package/dist/codegen-lang.d.ts +65 -0
- package/dist/codegen-lang.d.ts.map +1 -0
- package/dist/codegen-lang.js +526 -0
- package/dist/codegen-lang.js.map +1 -0
- package/dist/codegen-load.d.ts +31 -0
- package/dist/codegen-load.d.ts.map +1 -0
- package/dist/codegen-load.js +138 -0
- package/dist/codegen-load.js.map +1 -0
- package/dist/codegen-loaders.js +55 -0
- package/dist/codegen-loaders.js.map +1 -0
- package/dist/codegen-mcp.d.ts.map +1 -1
- package/dist/codegen-mcp.js +368 -0
- package/dist/codegen-mcp.js.map +1 -0
- package/dist/codegen-python.d.ts +13 -1
- package/dist/codegen-python.d.ts.map +1 -1
- package/dist/codegen-python.js +828 -0
- package/dist/codegen-python.js.map +1 -0
- package/dist/codegen-route-tree-intern.d.ts +15 -5
- package/dist/codegen-route-tree-intern.d.ts.map +1 -1
- package/dist/codegen-route-tree-intern.js +221 -0
- package/dist/codegen-route-tree-intern.js.map +1 -0
- package/dist/codegen-rust.d.ts.map +1 -1
- package/dist/codegen-rust.js +1293 -0
- package/dist/codegen-rust.js.map +1 -0
- package/dist/codegen-sanitize.d.ts.map +1 -1
- package/dist/codegen-sanitize.js +56 -0
- package/dist/codegen-sanitize.js.map +1 -0
- package/dist/codegen-schema-naming.d.ts +5 -0
- package/dist/codegen-schema-naming.d.ts.map +1 -1
- package/dist/codegen-schema-naming.js +150 -0
- package/dist/codegen-schema-naming.js.map +1 -0
- package/dist/codegen-sdk-model.d.ts +71 -0
- package/dist/codegen-sdk-model.d.ts.map +1 -0
- package/dist/codegen-sdk-model.js +179 -0
- package/dist/codegen-sdk-model.js.map +1 -0
- package/dist/codegen.d.ts +41 -23
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +4610 -0
- package/dist/codegen.js.map +1 -0
- package/dist/context.d.ts +36 -6
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +242 -0
- package/dist/context.js.map +1 -0
- package/dist/cookie-sign.d.ts +16 -2
- package/dist/cookie-sign.d.ts.map +1 -1
- package/dist/cookie-sign.js +104 -0
- package/dist/cookie-sign.js.map +1 -0
- package/dist/cookie.d.ts +12 -0
- package/dist/cookie.d.ts.map +1 -1
- package/dist/cookie.js +138 -0
- package/dist/cookie.js.map +1 -0
- package/dist/cors.d.ts +1 -0
- package/dist/cors.d.ts.map +1 -1
- package/dist/cors.js +87 -0
- package/dist/cors.js.map +1 -0
- package/dist/crypto.d.ts +6 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +38 -0
- package/dist/crypto.js.map +1 -0
- package/dist/csrf.d.ts +19 -0
- package/dist/csrf.d.ts.map +1 -1
- package/dist/csrf.js +79 -0
- package/dist/csrf.js.map +1 -0
- package/dist/curl-logger.d.ts +14 -3
- package/dist/curl-logger.d.ts.map +1 -1
- package/dist/curl-logger.js +199 -0
- package/dist/curl-logger.js.map +1 -0
- package/dist/detect-runtime.js +13 -0
- package/dist/detect-runtime.js.map +1 -0
- package/dist/dict.d.ts +11 -0
- package/dist/dict.d.ts.map +1 -0
- package/dist/dict.js +13 -0
- package/dist/dict.js.map +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +44 -0
- package/dist/error.js.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/etag.d.ts +2 -0
- package/dist/etag.d.ts.map +1 -1
- package/dist/etag.js +82 -0
- package/dist/etag.js.map +1 -0
- package/dist/feature-detect.d.ts +25 -0
- package/dist/feature-detect.d.ts.map +1 -0
- package/dist/feature-detect.js +144 -0
- package/dist/feature-detect.js.map +1 -0
- package/dist/feature-load.js +36 -0
- package/dist/feature-load.js.map +1 -0
- package/dist/feature-slots.d.ts +44 -0
- package/dist/feature-slots.d.ts.map +1 -0
- package/dist/feature-slots.js +48 -0
- package/dist/feature-slots.js.map +1 -0
- package/dist/fetch-request.d.ts +10 -0
- package/dist/fetch-request.d.ts.map +1 -0
- package/dist/fetch-request.js +13 -0
- package/dist/fetch-request.js.map +1 -0
- package/dist/gen-process.d.ts +15 -0
- package/dist/gen-process.d.ts.map +1 -0
- package/dist/gen-process.js +43 -0
- package/dist/gen-process.js.map +1 -0
- package/dist/gen-write.d.ts +19 -0
- package/dist/gen-write.d.ts.map +1 -0
- package/dist/gen-write.js +120 -0
- package/dist/gen-write.js.map +1 -0
- package/dist/glob.d.ts +21 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +115 -0
- package/dist/glob.js.map +1 -0
- package/dist/go-type-emitter.d.ts +18 -4
- package/dist/go-type-emitter.d.ts.map +1 -1
- package/dist/go-type-emitter.js +594 -0
- package/dist/go-type-emitter.js.map +1 -0
- package/dist/honey-response.d.ts.map +1 -1
- package/dist/honey-response.js +338 -0
- package/dist/honey-response.js.map +1 -0
- package/dist/i18n-slot.d.ts +8 -0
- package/dist/i18n-slot.d.ts.map +1 -1
- package/dist/i18n-slot.js +14 -0
- package/dist/i18n-slot.js.map +1 -0
- package/dist/i18n.d.ts +3 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +250 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +408 -148
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2324 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +140 -0
- package/dist/init.js.map +1 -0
- package/dist/input.js +4 -0
- package/dist/input.js.map +1 -0
- package/dist/invalidate-check.d.ts +11 -0
- package/dist/invalidate-check.d.ts.map +1 -1
- package/dist/invalidate-check.js +163 -0
- package/dist/invalidate-check.js.map +1 -0
- package/dist/invoke-user.d.ts +35 -0
- package/dist/invoke-user.d.ts.map +1 -0
- package/dist/invoke-user.js +73 -0
- package/dist/invoke-user.js.map +1 -0
- package/dist/ip-restrict.d.ts +13 -1
- package/dist/ip-restrict.d.ts.map +1 -1
- package/dist/ip-restrict.js +60 -0
- package/dist/ip-restrict.js.map +1 -0
- package/dist/ip.d.ts +39 -0
- package/dist/ip.d.ts.map +1 -0
- package/dist/ip.js +209 -0
- package/dist/ip.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +113 -0
- package/dist/logger.js.map +1 -0
- package/dist/media-type.d.ts +30 -0
- package/dist/media-type.d.ts.map +1 -0
- package/dist/media-type.js +97 -0
- package/dist/media-type.js.map +1 -0
- package/dist/meta-spec-merge.d.ts +32 -0
- package/dist/meta-spec-merge.d.ts.map +1 -0
- package/dist/meta-spec-merge.js +68 -0
- package/dist/meta-spec-merge.js.map +1 -0
- package/dist/meta-spec.d.ts +16 -3
- package/dist/meta-spec.d.ts.map +1 -1
- package/dist/meta-spec.js +535 -0
- package/dist/meta-spec.js.map +1 -0
- package/dist/middleware.d.ts +23 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +253 -0
- package/dist/middleware.js.map +1 -0
- package/dist/node-request.d.ts +57 -12
- package/dist/node-request.d.ts.map +1 -1
- package/dist/node-request.js +407 -0
- package/dist/node-request.js.map +1 -0
- package/dist/node.d.ts +20 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +396 -0
- package/dist/node.js.map +1 -0
- package/dist/openapi/collect.d.ts +6 -3
- package/dist/openapi/collect.d.ts.map +1 -1
- package/dist/openapi/collect.js +30 -0
- package/dist/openapi/collect.js.map +1 -0
- package/dist/openapi/docs-page.d.ts +42 -0
- package/dist/openapi/docs-page.d.ts.map +1 -0
- package/dist/openapi/docs-page.js +80 -0
- package/dist/openapi/docs-page.js.map +1 -0
- package/dist/openapi/document.d.ts +4 -0
- package/dist/openapi/document.d.ts.map +1 -1
- package/dist/openapi/document.js +785 -0
- package/dist/openapi/document.js.map +1 -0
- package/dist/openapi/json-schema-slot.d.ts +7 -0
- package/dist/openapi/json-schema-slot.d.ts.map +1 -1
- package/dist/openapi/json-schema-slot.js +38 -0
- package/dist/openapi/json-schema-slot.js.map +1 -0
- package/dist/openapi/register.d.ts.map +1 -1
- package/dist/openapi/register.js +20 -0
- package/dist/openapi/register.js.map +1 -0
- package/dist/openapi/scalar.d.ts +3 -1
- package/dist/openapi/scalar.d.ts.map +1 -1
- package/dist/openapi/scalar.js +21 -0
- package/dist/openapi/scalar.js.map +1 -0
- package/dist/openapi/serve.d.ts +9 -0
- package/dist/openapi/serve.d.ts.map +1 -0
- package/dist/openapi/serve.js +36 -0
- package/dist/openapi/serve.js.map +1 -0
- package/dist/openapi/spec-factory.d.ts +30 -1
- package/dist/openapi/spec-factory.d.ts.map +1 -1
- package/dist/openapi/spec-factory.js +52 -0
- package/dist/openapi/spec-factory.js.map +1 -0
- package/dist/openapi/spec.d.ts +14 -5
- package/dist/openapi/spec.d.ts.map +1 -1
- package/dist/openapi/spec.js +50 -0
- package/dist/openapi/spec.js.map +1 -0
- package/dist/openapi/swagger.d.ts +3 -1
- package/dist/openapi/swagger.d.ts.map +1 -1
- package/dist/openapi/swagger.js +22 -0
- package/dist/openapi/swagger.js.map +1 -0
- package/dist/pattern.d.ts +71 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +239 -0
- package/dist/pattern.js.map +1 -0
- package/dist/peer.d.ts +17 -0
- package/dist/peer.d.ts.map +1 -0
- package/dist/peer.js +50 -0
- package/dist/peer.js.map +1 -0
- package/dist/plugin.d.ts +67 -16
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +649 -0
- package/dist/plugin.js.map +1 -0
- package/dist/powered-by.d.ts.map +1 -1
- package/dist/powered-by.js +11 -0
- package/dist/powered-by.js.map +1 -0
- package/dist/pretty-json.d.ts.map +1 -1
- package/dist/pretty-json.js +47 -0
- package/dist/pretty-json.js.map +1 -0
- package/dist/producer-stream.d.ts +47 -0
- package/dist/producer-stream.d.ts.map +1 -0
- package/dist/producer-stream.js +267 -0
- package/dist/producer-stream.js.map +1 -0
- package/dist/proxy.d.ts +47 -8
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +274 -0
- package/dist/proxy.js.map +1 -0
- package/dist/python-type-emitter.d.ts +14 -1
- package/dist/python-type-emitter.d.ts.map +1 -1
- package/dist/python-type-emitter.js +157 -0
- package/dist/python-type-emitter.js.map +1 -0
- package/dist/realtime/bus.d.ts +17 -1
- package/dist/realtime/bus.d.ts.map +1 -1
- package/dist/realtime/bus.js +112 -0
- package/dist/realtime/bus.js.map +1 -0
- package/dist/realtime/register.d.ts +3 -0
- package/dist/realtime/register.d.ts.map +1 -0
- package/dist/realtime/register.js +10 -0
- package/dist/realtime/register.js.map +1 -0
- package/dist/realtime/route.d.ts +65 -26
- package/dist/realtime/route.d.ts.map +1 -1
- package/dist/realtime/route.js +72 -0
- package/dist/realtime/route.js.map +1 -0
- package/dist/realtime/server.d.ts +43 -0
- package/dist/realtime/server.d.ts.map +1 -0
- package/dist/realtime/server.js +255 -0
- package/dist/realtime/server.js.map +1 -0
- package/dist/request-id.d.ts +7 -0
- package/dist/request-id.d.ts.map +1 -1
- package/dist/request-id.js +16 -0
- package/dist/request-id.js.map +1 -0
- package/dist/request-limits.d.ts +3 -0
- package/dist/request-limits.d.ts.map +1 -0
- package/dist/request-limits.js +3 -0
- package/dist/request-limits.js.map +1 -0
- package/dist/request-path.d.ts +33 -0
- package/dist/request-path.d.ts.map +1 -0
- package/dist/request-path.js +178 -0
- package/dist/request-path.js.map +1 -0
- package/dist/request-to-curl.d.ts +7 -1
- package/dist/request-to-curl.d.ts.map +1 -1
- package/dist/request-to-curl.js +48 -0
- package/dist/request-to-curl.js.map +1 -0
- package/dist/response.d.ts +25 -2
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +313 -0
- package/dist/response.js.map +1 -0
- package/dist/rust-type-emitter.d.ts +14 -2
- package/dist/rust-type-emitter.d.ts.map +1 -1
- package/dist/rust-type-emitter.js +463 -0
- package/dist/rust-type-emitter.js.map +1 -0
- package/dist/secure-headers.d.ts.map +1 -1
- package/dist/secure-headers.js +53 -0
- package/dist/secure-headers.js.map +1 -0
- package/dist/serve-register.d.ts +4 -0
- package/dist/serve-register.d.ts.map +1 -1
- package/dist/serve-register.js +9 -0
- package/dist/serve-register.js.map +1 -0
- package/dist/serve-slot.d.ts +3 -0
- package/dist/serve-slot.d.ts.map +1 -1
- package/dist/serve-slot.js +52 -0
- package/dist/serve-slot.js.map +1 -0
- package/dist/serve.d.ts +18 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +157 -0
- package/dist/serve.js.map +1 -0
- package/dist/server-timing.d.ts.map +1 -1
- package/dist/server-timing.js +51 -0
- package/dist/server-timing.js.map +1 -0
- package/dist/static.d.ts +9 -0
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +102 -0
- package/dist/static.js.map +1 -0
- package/dist/telemetry/otel.d.ts +2 -0
- package/dist/telemetry/otel.d.ts.map +1 -1
- package/dist/telemetry/otel.js +135 -0
- package/dist/telemetry/otel.js.map +1 -0
- package/dist/testing.d.ts +12 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +124 -0
- package/dist/testing.js.map +1 -0
- package/dist/timeout.d.ts +6 -0
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +36 -0
- package/dist/timeout.js.map +1 -0
- package/dist/tree.d.ts +133 -36
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +437 -0
- package/dist/tree.js.map +1 -0
- package/dist/trust.d.ts +21 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +100 -0
- package/dist/trust.js.map +1 -0
- package/dist/ts-type-emitter.d.ts.map +1 -1
- package/dist/ts-type-emitter.js +150 -0
- package/dist/ts-type-emitter.js.map +1 -0
- package/dist/type-emitter.d.ts +16 -2
- package/dist/type-emitter.d.ts.map +1 -1
- package/dist/type-emitter.js +729 -0
- package/dist/type-emitter.js.map +1 -0
- package/dist/type-extractor.d.ts +8 -4
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +819 -0
- package/dist/type-extractor.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +96 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +14 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +335 -0
- package/dist/validation.js.map +1 -0
- package/dist/with-headers.d.ts +15 -0
- package/dist/with-headers.d.ts.map +1 -0
- package/dist/with-headers.js +45 -0
- package/dist/with-headers.js.map +1 -0
- package/dist/ws/bun.d.ts +11 -5
- package/dist/ws/bun.d.ts.map +1 -1
- package/dist/ws/bun.js +82 -0
- package/dist/ws/bun.js.map +1 -0
- package/dist/ws/cloudflare.d.ts +25 -3
- package/dist/ws/cloudflare.d.ts.map +1 -1
- package/dist/ws/cloudflare.js +117 -0
- package/dist/ws/cloudflare.js.map +1 -0
- package/dist/ws/deno.d.ts +4 -4
- package/dist/ws/deno.d.ts.map +1 -1
- package/dist/ws/deno.js +133 -0
- package/dist/ws/deno.js.map +1 -0
- package/dist/ws/node.d.ts +20 -3
- package/dist/ws/node.d.ts.map +1 -1
- package/dist/ws/node.js +173 -0
- package/dist/ws/node.js.map +1 -0
- package/dist/ws/session.d.ts +11 -0
- package/dist/ws/session.d.ts.map +1 -0
- package/dist/ws/session.js +60 -0
- package/dist/ws/session.js.map +1 -0
- package/dist/ws/shared.d.ts +65 -0
- package/dist/ws/shared.d.ts.map +1 -0
- package/dist/ws/shared.js +103 -0
- package/dist/ws/shared.js.map +1 -0
- package/dist/ws-origin.d.ts +20 -0
- package/dist/ws-origin.d.ts.map +1 -0
- package/dist/ws-origin.js +41 -0
- package/dist/ws-origin.js.map +1 -0
- package/dist/yaml.d.ts +15 -1
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +101 -0
- package/dist/yaml.js.map +1 -0
- package/docs/invalidate-spec.md +13 -1
- package/docs/meta-spec.md +36 -12
- package/docs/packaging.md +43 -24
- package/docs/regression-matrix/README.md +32 -0
- package/docs/regression-matrix/review.md +44 -0
- package/docs/regression-matrix/ws1-3.md +97 -0
- package/docs/regression-matrix/ws4-8.md +107 -0
- package/docs/regression-matrix/ws9-12.md +266 -0
- package/docs/sdk.md +102 -52
- package/package.json +183 -52
- package/src/accepts.ts +91 -33
- package/src/body-kind.ts +67 -0
- package/src/body-limit.ts +102 -87
- package/src/build/index.ts +104 -54
- package/src/cli-args.ts +123 -0
- package/src/cli-go/auth.go +12 -1
- package/src/cli-go/config.go +29 -2
- package/src/cli-go/errors.go +28 -10
- package/src/cli-go/multipart.go +51 -27
- package/src/cli-go/output.go +58 -4
- package/src/cli-go/root.go +1 -1
- package/src/cli-go/version.go +2 -2
- package/src/cli.ts +154 -148
- package/src/client/error.ts +56 -1
- package/src/client/http.ts +479 -256
- package/src/client/index.ts +55 -17
- package/src/client/path.ts +169 -0
- package/src/client/redirect-policy.ts +19 -0
- package/src/client/sdk.ts +180 -101
- package/src/client/sse.ts +92 -66
- package/src/client/ws.ts +67 -31
- package/src/client-go/errors.go +41 -12
- package/src/client-go/export.go +44 -0
- package/src/client-go/invalidation.go +52 -54
- package/src/client-go/nullable.go +62 -0
- package/src/client-go/realtime.go +147 -110
- package/src/client-go/runtime.go +646 -88
- package/src/client-go/sse.go +104 -94
- package/src/client-go/transport.go +67 -27
- package/src/client-go/ws.go +49 -25
- package/src/client-info.ts +79 -0
- package/src/client-mcp/server.ts +133 -3
- package/src/client-python/_invalidation.py +207 -291
- package/src/client-python/_realtime.py +63 -40
- package/src/client-python/_runtime.py +501 -119
- package/src/client-python/_sse.py +130 -74
- package/src/client-python/_transport.py +37 -9
- package/src/client-python/_ws.py +50 -22
- package/src/client-rust/errors.rs +21 -1
- package/src/client-rust/invalidation.rs +202 -231
- package/src/client-rust/invalidation_sync.rs +16 -229
- package/src/client-rust/nullable.rs +17 -0
- package/src/client-rust/realtime.rs +217 -217
- package/src/client-rust/runtime.rs +602 -177
- package/src/client-rust/runtime_sync.rs +169 -145
- package/src/client-rust/sse.rs +142 -121
- package/src/client-rust/ws.rs +61 -37
- package/src/codegen-go-cli.ts +607 -728
- package/src/codegen-go.ts +710 -770
- package/src/codegen-ir.ts +381 -75
- package/src/codegen-lang.ts +517 -0
- package/src/codegen-load.ts +150 -0
- package/src/codegen-mcp.ts +207 -108
- package/src/codegen-python.ts +684 -816
- package/src/codegen-route-tree-intern.ts +51 -25
- package/src/codegen-rust.ts +946 -1240
- package/src/codegen-sanitize.ts +0 -4
- package/src/codegen-schema-naming.ts +19 -6
- package/src/codegen-sdk-model.ts +228 -0
- package/src/codegen.ts +1488 -1140
- package/src/context.ts +130 -11
- package/src/cookie-sign.ts +85 -16
- package/src/cookie.ts +66 -11
- package/src/cors.ts +55 -54
- package/src/crypto.ts +27 -5
- package/src/csrf.ts +57 -25
- package/src/curl-logger.ts +155 -114
- package/src/dict.ts +12 -0
- package/src/error.ts +4 -2
- package/src/etag.ts +58 -34
- package/src/feature-detect.ts +147 -0
- package/src/feature-slots.ts +70 -0
- package/src/fetch-request.ts +13 -0
- package/src/gen-process.ts +42 -0
- package/src/gen-write.ts +127 -0
- package/src/glob.ts +109 -0
- package/src/go-type-emitter.ts +406 -155
- package/src/honey-response.ts +36 -9
- package/src/i18n-slot.ts +10 -0
- package/src/i18n.ts +98 -23
- package/src/index.ts +1887 -1756
- package/src/init.ts +40 -28
- package/src/invalidate-check.ts +42 -4
- package/src/invoke-user.ts +95 -0
- package/src/ip-restrict.ts +47 -161
- package/src/ip.ts +194 -0
- package/src/logger.ts +65 -36
- package/src/media-type.ts +104 -0
- package/src/meta-spec-merge.ts +80 -0
- package/src/meta-spec.ts +0 -0
- package/src/middleware.ts +112 -12
- package/src/node-request.ts +242 -52
- package/src/node.ts +318 -75
- package/src/openapi/collect.ts +19 -44
- package/src/openapi/docs-page.ts +89 -0
- package/src/openapi/document.ts +503 -224
- package/src/openapi/json-schema-slot.ts +40 -0
- package/src/openapi/register.ts +7 -2
- package/src/openapi/scalar.ts +11 -10
- package/src/openapi/serve.ts +39 -0
- package/src/openapi/spec-factory.ts +50 -1
- package/src/openapi/spec.ts +53 -30
- package/src/openapi/swagger.ts +12 -11
- package/src/pattern.ts +245 -0
- package/src/peer.ts +53 -0
- package/src/plugin.ts +270 -185
- package/src/powered-by.ts +2 -2
- package/src/pretty-json.ts +25 -9
- package/src/producer-stream.ts +296 -0
- package/src/proxy.ts +253 -55
- package/src/python-type-emitter.ts +68 -56
- package/src/realtime/bus.ts +56 -9
- package/src/realtime/register.ts +13 -0
- package/src/realtime/route.ts +129 -80
- package/src/realtime/server.ts +298 -0
- package/src/request-id.ts +13 -3
- package/src/request-limits.ts +2 -0
- package/src/request-path.ts +165 -0
- package/src/request-to-curl.ts +28 -7
- package/src/response.ts +143 -103
- package/src/rust-type-emitter.ts +265 -170
- package/src/secure-headers.ts +8 -4
- package/src/serve-register.ts +4 -0
- package/src/serve-slot.ts +38 -0
- package/src/serve.ts +113 -25
- package/src/server-timing.ts +11 -7
- package/src/static.ts +85 -32
- package/src/telemetry/otel.ts +29 -7
- package/src/testing.ts +76 -14
- package/src/timeout.ts +15 -7
- package/src/tree.ts +405 -333
- package/src/trust.ts +117 -0
- package/src/ts-type-emitter.ts +18 -12
- package/src/type-emitter.ts +227 -89
- package/src/type-extractor.ts +603 -423
- package/src/types.ts +8 -0
- package/src/validation.ts +221 -96
- package/src/with-headers.ts +45 -0
- package/src/ws/bun.ts +53 -15
- package/src/ws/cloudflare.ts +81 -19
- package/src/ws/deno.ts +124 -36
- package/src/ws/node.ts +173 -79
- package/src/ws/session.ts +73 -0
- package/src/ws/shared.ts +140 -0
- package/src/ws-origin.ts +53 -0
- package/src/yaml.ts +45 -22
- package/dist/client-mcp/server.d.ts +0 -10
- package/dist/client-mcp/server.d.ts.map +0 -1
- package/dist/client-mcp/types.d.ts +0 -16
- package/dist/client-mcp/types.d.ts.map +0 -1
- package/dist/cloudflare/realtime.d.ts +0 -30
- package/dist/cloudflare/realtime.d.ts.map +0 -1
- package/dist/realtime/buffer.d.ts +0 -18
- package/dist/realtime/buffer.d.ts.map +0 -1
- package/dist/realtime/longpoll.d.ts +0 -23
- package/dist/realtime/longpoll.d.ts.map +0 -1
- package/dist/realtime/protocol.d.ts +0 -50
- package/dist/realtime/protocol.d.ts.map +0 -1
- package/dist/realtime/runtime.d.ts +0 -74
- package/dist/realtime/runtime.d.ts.map +0 -1
- package/src/cloudflare/realtime.ts +0 -234
- package/src/realtime/buffer.ts +0 -101
- package/src/realtime/longpoll.ts +0 -148
- package/src/realtime/protocol.ts +0 -128
- package/src/realtime/runtime.ts +0 -427
package/src/openapi/document.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { deriveSchemaName, sanitizeComponentName } from "../codegen-schema-naming.ts"
|
|
2
|
+
import type { HoneyError } from "../error.ts"
|
|
2
3
|
import { ERROR_META } from "../errors.ts"
|
|
3
4
|
import type { ErrorMetaEntry } from "../errors.ts"
|
|
4
5
|
import type { InvalidateCheckConfig, InvalidateCheckOperation } from "../invalidate-check.ts"
|
|
5
|
-
import { reportMissingInvalidate } from "../invalidate-check.ts"
|
|
6
|
+
import { findInvalidSelectors, invalidateLevel, reportMissingInvalidate } from "../invalidate-check.ts"
|
|
6
7
|
import {
|
|
7
8
|
applyMetaSpec,
|
|
8
9
|
compileMetaSpec,
|
|
9
10
|
MetaSpecCollector,
|
|
10
11
|
resolveProfile,
|
|
12
|
+
securitySchemeNames,
|
|
11
13
|
type SchemaMetaHit,
|
|
12
14
|
type SchemaMetaLookup,
|
|
13
15
|
} from "../meta-spec.ts"
|
|
14
|
-
import
|
|
16
|
+
import { metaSpecOf } from "../meta-spec-merge.ts"
|
|
17
|
+
import { parsePattern, UNNAMED_WILDCARD } from "../pattern.ts"
|
|
18
|
+
import type { RouteHandler } from "../tree.ts"
|
|
15
19
|
import type {
|
|
16
20
|
InputSchemaEntry,
|
|
17
21
|
InputSchemasDef,
|
|
@@ -20,8 +24,8 @@ import type {
|
|
|
20
24
|
StandardSchemaLike,
|
|
21
25
|
} from "../types.ts"
|
|
22
26
|
import { statusKeyToCode } from "../types.ts"
|
|
23
|
-
import {
|
|
24
|
-
import { getJsonSchemaConverter } from "./json-schema-slot.ts"
|
|
27
|
+
import { collectRoutes, collectWsRoutes, toOpenApiPath, unwrapEntry } from "./collect.ts"
|
|
28
|
+
import { getJsonSchemaConverter, instanceJsonSchema } from "./json-schema-slot.ts"
|
|
25
29
|
|
|
26
30
|
export type OpenApiRouteInfo<TMeta = unknown> = {
|
|
27
31
|
meta: TMeta
|
|
@@ -43,6 +47,7 @@ export type OpenApiSpec = {
|
|
|
43
47
|
info: OpenApiInfo
|
|
44
48
|
openapi: string
|
|
45
49
|
paths: Record<string, Record<string, Record<string, unknown>>>
|
|
50
|
+
servers?: Array<{ description?: string; url: string }>
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export const DEFAULT_ERROR_JSON_SCHEMA: Record<string, unknown> = {
|
|
@@ -123,7 +128,7 @@ function getErrorMeta(factory: Record<string, () => HoneyError> | null): Record<
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
function getMetaSpecConfig(app: unknown): MetaSpecConfig | null {
|
|
126
|
-
return (app
|
|
131
|
+
return metaSpecOf(app)
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
function cloneJson<T>(value: T): T {
|
|
@@ -180,9 +185,8 @@ function asJsonSchema(
|
|
|
180
185
|
io: "input" | "output" = "output",
|
|
181
186
|
): Record<string, unknown> {
|
|
182
187
|
if (entry !== null && entry !== undefined && "~standard" in (entry as object)) {
|
|
183
|
-
const convert = getJsonSchemaConverter()
|
|
184
|
-
|
|
185
|
-
return {}
|
|
188
|
+
const convert = getJsonSchemaConverter() ?? instanceJsonSchema
|
|
189
|
+
return convert(entry as StandardSchemaLike, io)
|
|
186
190
|
}
|
|
187
191
|
return entry as Record<string, unknown>
|
|
188
192
|
}
|
|
@@ -286,6 +290,224 @@ function makeSchemaMetaLookup(handler: RouteHandler): SchemaMetaLookup {
|
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
292
|
|
|
293
|
+
/** Methods an OpenAPI path item can hold. Anything else cannot be described and is skipped. */
|
|
294
|
+
const OPENAPI_METHODS = new Set(["delete", "get", "head", "options", "patch", "post", "put", "trace"])
|
|
295
|
+
|
|
296
|
+
/** What `app.all()` documents: every method a client would send to it, unless the path declares that method itself. */
|
|
297
|
+
const ALL_METHODS = ["get", "post", "put", "patch", "delete"] as const
|
|
298
|
+
|
|
299
|
+
type AppSettingsView = { stripPrefix?: string | null; trailingSlash?: "enforce" | "ignore" | "strip" }
|
|
300
|
+
|
|
301
|
+
function appSettings(app: unknown): AppSettingsView {
|
|
302
|
+
return (app as { _graph?: { settings?: AppSettingsView } })._graph?.settings ?? {}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** The URL path a client sends for a route path: `trailingSlash("enforce")` adds a slash. */
|
|
306
|
+
function publicPath(path: string, settings: AppSettingsView): string {
|
|
307
|
+
if (settings.trailingSlash === "enforce" && path.length > 1 && !path.endsWith("/")) return `${path}/`
|
|
308
|
+
return path
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function isMetaInternal(meta: Record<string, unknown> | null): boolean {
|
|
312
|
+
return meta?.internal === true
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Path parameters of a concrete route path. Every path parameter is required in OpenAPI. */
|
|
316
|
+
function pathParameters(path: string): Array<Record<string, unknown>> {
|
|
317
|
+
const out: Array<Record<string, unknown>> = []
|
|
318
|
+
for (const seg of parsePattern(path)) {
|
|
319
|
+
if (seg.k === "param") {
|
|
320
|
+
out.push({ in: "path", name: seg.n, required: true, schema: { type: "string" } })
|
|
321
|
+
} else if (seg.k === "wildcard") {
|
|
322
|
+
out.push({
|
|
323
|
+
description: "The rest of the path; may contain `/`.",
|
|
324
|
+
in: "path",
|
|
325
|
+
name: seg.n === UNNAMED_WILDCARD ? "wildcard" : seg.n,
|
|
326
|
+
required: true,
|
|
327
|
+
schema: { type: "string" },
|
|
328
|
+
"x-honey-wildcard": true,
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return out
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
type ContentMap = Record<string, { schema: Record<string, unknown> }>
|
|
336
|
+
|
|
337
|
+
/** Add a response, merging content types and headers into one already declared for the status. */
|
|
338
|
+
function addResponse(
|
|
339
|
+
responses: Record<string, Record<string, unknown>>,
|
|
340
|
+
status: string,
|
|
341
|
+
description: string,
|
|
342
|
+
content: ContentMap | null,
|
|
343
|
+
headers: Record<string, unknown> | null,
|
|
344
|
+
): void {
|
|
345
|
+
const existing = responses[status]
|
|
346
|
+
if (existing === undefined) {
|
|
347
|
+
const response: Record<string, unknown> = {}
|
|
348
|
+
if (content) response.content = content
|
|
349
|
+
response.description = description
|
|
350
|
+
if (headers) response.headers = headers
|
|
351
|
+
responses[status] = response
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
if (headers) existing.headers = { ...(existing.headers as Record<string, unknown> | undefined), ...headers }
|
|
355
|
+
if (content) existing.content = { ...(existing.content as ContentMap | undefined), ...content }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Turn a converted schema's local references into component references.
|
|
360
|
+
*
|
|
361
|
+
* Converters write `$ref: "#"` for recursion and `#/$defs/<name>` for shared nodes — relative
|
|
362
|
+
* to the schema's own root. In an OpenAPI document `#` is the document, so each is hoisted into
|
|
363
|
+
* `components.schemas` and the reference rewritten to point at it.
|
|
364
|
+
*/
|
|
365
|
+
class LocalRefHoister {
|
|
366
|
+
readonly components: Record<string, Record<string, unknown>> = Object.create(null)
|
|
367
|
+
|
|
368
|
+
private readonly taken: (name: string) => boolean
|
|
369
|
+
|
|
370
|
+
constructor(taken: (name: string) => boolean) {
|
|
371
|
+
this.taken = taken
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private reserve(base: string): string {
|
|
375
|
+
const clean = sanitizeComponentName(base) || "Schema"
|
|
376
|
+
let name = clean
|
|
377
|
+
for (let n = 2; this.taken(name) || Object.hasOwn(this.components, name); n++) name = `${clean}${n}`
|
|
378
|
+
this.components[name] = {}
|
|
379
|
+
return name
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
hoist(schema: Record<string, unknown>, hint: string): Record<string, unknown> {
|
|
383
|
+
const defs = schema.$defs ?? schema.definitions
|
|
384
|
+
const hasDefs = defs !== null && typeof defs === "object" && Object.keys(defs).length > 0
|
|
385
|
+
const selfRef = refersTo(schema, "#")
|
|
386
|
+
if (!hasDefs && !selfRef) return schema
|
|
387
|
+
|
|
388
|
+
const rootName = selfRef ? this.reserve(hint) : null
|
|
389
|
+
const defNames = new Map<string, string>()
|
|
390
|
+
if (hasDefs) {
|
|
391
|
+
for (const key of Object.keys(defs as Record<string, unknown>)) {
|
|
392
|
+
defNames.set(key, this.reserve(key.startsWith("__schema") ? `${hint}${key.slice(8)}` : key))
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const rewrite = (ref: string): string | null => {
|
|
396
|
+
if (ref === "#" && rootName !== null) return `#/components/schemas/${rootName}`
|
|
397
|
+
for (const prefix of ["#/$defs/", "#/definitions/"]) {
|
|
398
|
+
if (!ref.startsWith(prefix)) continue
|
|
399
|
+
const name = defNames.get(decodePointer(ref.slice(prefix.length)))
|
|
400
|
+
if (name !== undefined) return `#/components/schemas/${name}`
|
|
401
|
+
}
|
|
402
|
+
return null
|
|
403
|
+
}
|
|
404
|
+
for (const [key, name] of defNames) {
|
|
405
|
+
this.components[name] = rewriteRefs((defs as Record<string, Record<string, unknown>>)[key], rewrite)
|
|
406
|
+
}
|
|
407
|
+
const { $defs: _defs, definitions: _definitions, ...rest } = schema
|
|
408
|
+
const root = rewriteRefs(rest, rewrite)
|
|
409
|
+
if (rootName === null) return root
|
|
410
|
+
this.components[rootName] = root
|
|
411
|
+
return { $ref: `#/components/schemas/${rootName}` }
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function decodePointer(token: string): string {
|
|
416
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~")
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Keys whose values are data, not schemas: a `$ref` inside them is a value, not a reference. */
|
|
420
|
+
const DATA_KEYS = new Set(["const", "default", "enum", "example", "examples"])
|
|
421
|
+
|
|
422
|
+
function refersTo(node: unknown, target: string): boolean {
|
|
423
|
+
if (Array.isArray(node)) return node.some((item) => refersTo(item, target))
|
|
424
|
+
if (node === null || typeof node !== "object") return false
|
|
425
|
+
for (const [key, value] of Object.entries(node)) {
|
|
426
|
+
if (key === "$ref" && value === target) return true
|
|
427
|
+
if (DATA_KEYS.has(key)) continue
|
|
428
|
+
if (refersTo(value, target)) return true
|
|
429
|
+
}
|
|
430
|
+
return false
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function rewriteRefs(node: unknown, rewrite: (ref: string) => string | null): Record<string, unknown> {
|
|
434
|
+
const walk = (value: unknown): unknown => {
|
|
435
|
+
if (Array.isArray(value)) return value.map(walk)
|
|
436
|
+
if (value === null || typeof value !== "object") return value
|
|
437
|
+
const out: Record<string, unknown> = {}
|
|
438
|
+
for (const [key, child] of Object.entries(value)) {
|
|
439
|
+
if (key === "$ref" && typeof child === "string") {
|
|
440
|
+
out[key] = rewrite(child) ?? child
|
|
441
|
+
} else if (DATA_KEYS.has(key)) {
|
|
442
|
+
out[key] = child
|
|
443
|
+
} else {
|
|
444
|
+
out[key] = walk(child)
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return out
|
|
448
|
+
}
|
|
449
|
+
return walk(node) as Record<string, unknown>
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function operationIdOf(operation: Record<string, unknown>): string | null {
|
|
453
|
+
return typeof operation.operationId === "string" ? operation.operationId : null
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
type EmittedOperation = {
|
|
457
|
+
handler: object
|
|
458
|
+
/** What one registration shares across its operations: the handler function */
|
|
459
|
+
source: unknown
|
|
460
|
+
method: string
|
|
461
|
+
operation: Record<string, unknown>
|
|
462
|
+
path: string
|
|
463
|
+
/** Path params beyond the route's shortest variant, for optional-param suffixes */
|
|
464
|
+
params: string[]
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Make `operationId`s unique. One route that the document expands into several operations
|
|
469
|
+
* (`.on(["GET","POST"])`, `all()`, an optional param) keeps one meta `operationId`; each
|
|
470
|
+
* operation gets a method or variant suffix. Two different routes declaring the same id is
|
|
471
|
+
* an authoring error.
|
|
472
|
+
*/
|
|
473
|
+
function assignOperationIds(emitted: readonly EmittedOperation[], collector: MetaSpecCollector): void {
|
|
474
|
+
const byId = new Map<string, EmittedOperation[]>()
|
|
475
|
+
for (const entry of emitted) {
|
|
476
|
+
const id = operationIdOf(entry.operation)
|
|
477
|
+
if (id === null) continue
|
|
478
|
+
const list = byId.get(id)
|
|
479
|
+
if (list) list.push(entry)
|
|
480
|
+
else byId.set(id, [entry])
|
|
481
|
+
}
|
|
482
|
+
const used = new Set(byId.keys())
|
|
483
|
+
for (const [id, group] of byId) {
|
|
484
|
+
if (group.length < 2) continue
|
|
485
|
+
/* `.on([...])` registers one record per method around one handler function */
|
|
486
|
+
const handlers = new Set(group.map((g) => g.source))
|
|
487
|
+
if (handlers.size > 1) {
|
|
488
|
+
const where = group.map((g) => `${g.method.toUpperCase()} ${g.path}`).join(", ")
|
|
489
|
+
collector.add(
|
|
490
|
+
"DUPLICATE_OPERATION_ID",
|
|
491
|
+
"error",
|
|
492
|
+
`operationId "${id}" is declared by more than one route (${where}); operationIds must be unique`,
|
|
493
|
+
)
|
|
494
|
+
continue
|
|
495
|
+
}
|
|
496
|
+
const methods = new Set(group.map((g) => g.method))
|
|
497
|
+
const minParams = Math.min(...group.map((g) => g.params.length))
|
|
498
|
+
for (const entry of group) {
|
|
499
|
+
let next = id
|
|
500
|
+
if (methods.size > 1) next += `_${entry.method}`
|
|
501
|
+
const extra = entry.params.slice(minParams)
|
|
502
|
+
if (extra.length > 0) next += `_with_${extra.join("_")}`
|
|
503
|
+
if (next === id) continue
|
|
504
|
+
while (used.has(next)) next += "_"
|
|
505
|
+
used.add(next)
|
|
506
|
+
entry.operation.operationId = next
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
289
511
|
export function generateOpenApiFromTree<TMeta = unknown>(
|
|
290
512
|
app: unknown,
|
|
291
513
|
options: {
|
|
@@ -301,6 +523,8 @@ export function generateOpenApiFromTree<TMeta = unknown>(
|
|
|
301
523
|
const { filter: profileFilter, name: profileName } = resolveProfile(metaSpec, options.profile, collector)
|
|
302
524
|
const factory = getErrorFactory(app)
|
|
303
525
|
const errorMeta = getErrorMeta(factory)
|
|
526
|
+
const settings = appSettings(app)
|
|
527
|
+
const hoister = new LocalRefHoister(() => false)
|
|
304
528
|
|
|
305
529
|
const rawErrorSchema = getErrorSchema(app)
|
|
306
530
|
const baseErrorJsonSchema = rawErrorSchema ? asJsonSchema(rawErrorSchema) : DEFAULT_ERROR_JSON_SCHEMA
|
|
@@ -308,107 +532,87 @@ export function generateOpenApiFromTree<TMeta = unknown>(
|
|
|
308
532
|
const rawCustomErrorSchema = getCustomErrorSchema(app)
|
|
309
533
|
const customErrorAddsSchema = rawCustomErrorSchema ? asJsonSchema(rawCustomErrorSchema) : null
|
|
310
534
|
|
|
311
|
-
const
|
|
312
|
-
const collected: Array<{ handler: RouteHandler; method: string; path: string }> = []
|
|
313
|
-
walkTree(tree, "", collected)
|
|
535
|
+
const collected = collectRoutes(app)
|
|
314
536
|
|
|
315
537
|
const routeFilter = options.filterRoutes
|
|
316
|
-
const paths: Record<string, Record<string, Record<string, unknown>>> =
|
|
538
|
+
const paths: Record<string, Record<string, Record<string, unknown>>> = Object.create(null)
|
|
317
539
|
const emitted: InvalidateCheckOperation[] = []
|
|
540
|
+
const operations: EmittedOperation[] = []
|
|
318
541
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const operation: Record<string, unknown> = {}
|
|
331
|
-
const responses: Record<string, unknown> = {}
|
|
332
|
-
const parameters: Array<Record<string, unknown>> = []
|
|
333
|
-
|
|
334
|
-
applyMetaSpec({
|
|
335
|
-
collector,
|
|
336
|
-
filter: profileFilter,
|
|
337
|
-
kind: "http",
|
|
338
|
-
meta: handler.mt as Record<string, unknown> | null,
|
|
339
|
-
method,
|
|
340
|
-
operation,
|
|
341
|
-
path,
|
|
342
|
-
profile: profileName,
|
|
343
|
-
schemaMeta: metaSpec.needsSchemas ? makeSchemaMetaLookup(handler) : undefined,
|
|
344
|
-
spec: metaSpec,
|
|
345
|
-
})
|
|
542
|
+
/* explicit methods per path — `all()` documents only the methods nobody declared */
|
|
543
|
+
const declared = new Set<string>()
|
|
544
|
+
for (const { method, path } of collected) {
|
|
545
|
+
if (method !== "ALL") declared.add(`${method.toLowerCase()} ${path}`)
|
|
546
|
+
}
|
|
547
|
+
const shortestParams = new Map<object, number>()
|
|
548
|
+
for (const { handler, path } of collected) {
|
|
549
|
+
const n = pathParameters(path).length
|
|
550
|
+
const prev = shortestParams.get(handler)
|
|
551
|
+
if (prev === undefined || n < prev) shortestParams.set(handler, n)
|
|
552
|
+
}
|
|
346
553
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
554
|
+
for (const { handler, method, path } of collected) {
|
|
555
|
+
const meta = (handler.mt ?? null) as Record<string, unknown> | null
|
|
556
|
+
if (isMetaInternal(meta)) continue
|
|
557
|
+
if (routeFilter && !routeFilter({ meta: (meta ?? {}) as TMeta, method, path })) continue
|
|
558
|
+
|
|
559
|
+
const methods =
|
|
560
|
+
method === "ALL"
|
|
561
|
+
? ALL_METHODS.filter((m) => !declared.has(`${m} ${path}`))
|
|
562
|
+
: [method.toLowerCase()].filter((m) => {
|
|
563
|
+
if (OPENAPI_METHODS.has(m)) return true
|
|
564
|
+
collector.add(
|
|
565
|
+
"UNSUPPORTED_METHOD",
|
|
566
|
+
"warn",
|
|
567
|
+
`${method} ${path}: OpenAPI cannot describe the ${method} method; the route is left out of the document`,
|
|
568
|
+
)
|
|
569
|
+
return false
|
|
570
|
+
})
|
|
571
|
+
if (methods.length === 0) continue
|
|
572
|
+
|
|
573
|
+
const oaPath = publicPath(toOpenApiPath(path), settings)
|
|
574
|
+
const params = pathParameters(path)
|
|
575
|
+
const schemaHint = (role: "request" | "response", status?: number): string =>
|
|
576
|
+
role === "request"
|
|
577
|
+
? deriveSchemaName({ method: methods[0], path: oaPath, role })
|
|
578
|
+
: deriveSchemaName({ method: methods[0], path: oaPath, role, status: status ?? 200 })
|
|
579
|
+
|
|
580
|
+
/* the parts that do not depend on the method are built once per route */
|
|
581
|
+
const parameters: Array<Record<string, unknown>> = params.map((p) => ({ ...p }))
|
|
582
|
+
let requestBody: Record<string, unknown> | null = null
|
|
583
|
+
const responses: Record<string, Record<string, unknown>> = {}
|
|
357
584
|
|
|
358
585
|
if (handler.iv) {
|
|
586
|
+
const body: ContentMap = {}
|
|
359
587
|
for (const [source, entry] of Object.entries(handler.iv)) {
|
|
360
588
|
if (entry === undefined) continue
|
|
361
589
|
const unwrapped = unwrapEntry(entry as InputSchemaEntry)
|
|
362
590
|
const jsonSchema = asJsonSchema(unwrapped, "input")
|
|
363
591
|
|
|
364
592
|
if (source === "json" || source === "form") {
|
|
365
|
-
const schema = stripInternalProps(jsonSchema)
|
|
366
|
-
const content: Record<string, { schema: Record<string, unknown> }> = {}
|
|
593
|
+
const schema = hoister.hoist(stripInternalProps(jsonSchema), schemaHint("request"))
|
|
367
594
|
if (source === "form") {
|
|
368
|
-
if (formSchemaHasBinaryPart(
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
content["application/x-www-form-urlencoded"] = { schema }
|
|
595
|
+
if (formSchemaHasBinaryPart(jsonSchema)) body["multipart/form-data"] = { schema }
|
|
596
|
+
body["application/x-www-form-urlencoded"] = { schema }
|
|
372
597
|
} else {
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
operation.requestBody = {
|
|
376
|
-
content,
|
|
377
|
-
required: true,
|
|
598
|
+
body["application/json"] = { schema }
|
|
378
599
|
}
|
|
379
600
|
} else if (source === "search" || source === "headers" || source === "cookies") {
|
|
380
601
|
let location: "cookie" | "header" | "query" = "cookie"
|
|
381
602
|
if (source === "search") location = "query"
|
|
382
603
|
else if (source === "headers") location = "header"
|
|
383
|
-
|
|
384
|
-
const required = (jsonSchema.required as string[]) ?? []
|
|
385
|
-
if (props) {
|
|
386
|
-
for (const [propName, propSchema] of Object.entries(props)) {
|
|
387
|
-
if ((propSchema as Record<string, unknown>)?.["x-internal"] === true) continue
|
|
388
|
-
parameters.push({
|
|
389
|
-
in: location,
|
|
390
|
-
name: propName,
|
|
391
|
-
required: required.includes(propName),
|
|
392
|
-
schema: propSchema,
|
|
393
|
-
})
|
|
394
|
-
}
|
|
395
|
-
}
|
|
604
|
+
parameters.push(...propertyParameters(jsonSchema, location, hoister, schemaHint("request")))
|
|
396
605
|
} else if (source === "params") {
|
|
397
606
|
const props = jsonSchema.properties as Record<string, unknown> | undefined
|
|
398
607
|
if (props) {
|
|
399
608
|
for (const param of parameters) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
param.schema = propSchema
|
|
403
|
-
}
|
|
609
|
+
if (param.in !== "path" || !Object.hasOwn(props, param.name as string)) continue
|
|
610
|
+
param.schema = props[param.name as string]
|
|
404
611
|
}
|
|
405
612
|
}
|
|
406
613
|
}
|
|
407
614
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (parameters.length > 0) {
|
|
411
|
-
operation.parameters = parameters
|
|
615
|
+
if (Object.keys(body).length > 0) requestBody = { content: body, required: true }
|
|
412
616
|
}
|
|
413
617
|
|
|
414
618
|
if (handler.os) {
|
|
@@ -417,138 +621,106 @@ export function generateOpenApiFromTree<TMeta = unknown>(
|
|
|
417
621
|
if (contentType === "redirect") {
|
|
418
622
|
for (const statusKey of Object.keys(schemas)) {
|
|
419
623
|
const statusCode = statusKeyToCode[statusKey as keyof typeof statusKeyToCode]
|
|
420
|
-
if (statusCode)
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
}
|
|
430
|
-
}
|
|
624
|
+
if (!statusCode) continue
|
|
625
|
+
addResponse(responses, String(statusCode), statusKey.replace(/_/g, " "), null, {
|
|
626
|
+
Location: {
|
|
627
|
+
description: "Redirect target URL",
|
|
628
|
+
schema: { format: "uri", type: "string" },
|
|
629
|
+
},
|
|
630
|
+
})
|
|
431
631
|
}
|
|
432
632
|
continue
|
|
433
633
|
}
|
|
434
634
|
for (const [statusKey, schema] of Object.entries(schemas)) {
|
|
435
635
|
if (schema === undefined) continue
|
|
436
636
|
const statusCode = statusKeyToCode[statusKey as keyof typeof statusKeyToCode]
|
|
437
|
-
if (statusCode)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
637
|
+
if (!statusCode) continue
|
|
638
|
+
const converted = asJsonSchema(schema as StandardSchemaLike | Record<string, unknown>)
|
|
639
|
+
addResponse(
|
|
640
|
+
responses,
|
|
641
|
+
String(statusCode),
|
|
642
|
+
statusKey.replace(/_/g, " "),
|
|
643
|
+
{ [contentType]: { schema: hoister.hoist(converted, schemaHint("response", statusCode)) } },
|
|
644
|
+
null,
|
|
645
|
+
)
|
|
447
646
|
}
|
|
448
647
|
}
|
|
449
648
|
}
|
|
450
649
|
|
|
451
650
|
if (handler.ek.size > 0) {
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
byStatus.set(info.status, entries)
|
|
461
|
-
}
|
|
462
|
-
const meta = errorMeta?.[ek]
|
|
463
|
-
let customSchema: Record<string, unknown> | null = null
|
|
464
|
-
if (meta?.schema) {
|
|
465
|
-
const converted = asJsonSchema(meta.schema as StandardSchemaLike)
|
|
466
|
-
if (converted && Object.keys(converted).length > 0) {
|
|
467
|
-
customSchema = customErrorAddsSchema ? { allOf: [converted, customErrorAddsSchema] } : converted
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
entries.push({ key: ek, schema: customSchema })
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
for (const [status, entries] of byStatus) {
|
|
474
|
-
if (responses[String(status)] === undefined) {
|
|
475
|
-
const standardKeys = entries.filter((e) => !e.schema).map((e) => e.key)
|
|
476
|
-
const customSchemas = entries.filter((e) => e.schema).map((e) => e.schema as Record<string, unknown>)
|
|
477
|
-
|
|
478
|
-
let schema: Record<string, unknown>
|
|
479
|
-
|
|
480
|
-
if (customSchemas.length === 0) {
|
|
481
|
-
schema = cloneJson(baseErrorJsonSchema)
|
|
482
|
-
const props = schema.properties as Record<string, unknown> | undefined
|
|
483
|
-
if (props?.error_key) {
|
|
484
|
-
props.error_key = { enum: standardKeys.sort(), type: "string" }
|
|
485
|
-
}
|
|
486
|
-
if (props?.status) {
|
|
487
|
-
props.status = { enum: [status], type: "integer" }
|
|
488
|
-
}
|
|
489
|
-
} else if (standardKeys.length === 0 && customSchemas.length === 1) {
|
|
490
|
-
schema = customSchemas[0]
|
|
491
|
-
} else {
|
|
492
|
-
const schemas: Record<string, unknown>[] = []
|
|
493
|
-
if (standardKeys.length > 0) {
|
|
494
|
-
const stdSchema = cloneJson(baseErrorJsonSchema)
|
|
495
|
-
const props = stdSchema.properties as Record<string, unknown> | undefined
|
|
496
|
-
if (props?.error_key) {
|
|
497
|
-
props.error_key = { enum: standardKeys.sort(), type: "string" }
|
|
498
|
-
}
|
|
499
|
-
if (props?.status) {
|
|
500
|
-
props.status = { enum: [status], type: "integer" }
|
|
501
|
-
}
|
|
502
|
-
schemas.push(stdSchema)
|
|
503
|
-
}
|
|
504
|
-
schemas.push(...customSchemas)
|
|
505
|
-
schema = { oneOf: schemas }
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
responses[String(status)] = {
|
|
509
|
-
content: {
|
|
510
|
-
"application/json": { schema },
|
|
511
|
-
},
|
|
512
|
-
description: entries
|
|
513
|
-
.map((e) => e.key)
|
|
514
|
-
.sort()
|
|
515
|
-
.join(", "),
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
651
|
+
/* a mounted sub-app's route keeps its own error factory */
|
|
652
|
+
const routeFactory = (handler.fac as Record<string, () => HoneyError> | null | undefined) ?? factory
|
|
653
|
+
addErrorResponses(responses, handler, {
|
|
654
|
+
baseErrorJsonSchema,
|
|
655
|
+
customErrorAddsSchema,
|
|
656
|
+
errorMeta: routeFactory === factory ? errorMeta : getErrorMeta(routeFactory),
|
|
657
|
+
factory: routeFactory,
|
|
658
|
+
})
|
|
519
659
|
}
|
|
520
660
|
|
|
521
661
|
if (Object.keys(responses).length === 0) {
|
|
522
662
|
responses["200"] = { description: "Success" }
|
|
523
663
|
}
|
|
524
|
-
operation.responses = responses
|
|
525
664
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
665
|
+
for (const methodKey of methods) {
|
|
666
|
+
const operation: Record<string, unknown> = {}
|
|
667
|
+
|
|
668
|
+
applyMetaSpec({
|
|
669
|
+
collector,
|
|
670
|
+
filter: profileFilter,
|
|
671
|
+
kind: "http",
|
|
672
|
+
meta,
|
|
673
|
+
method: methodKey.toUpperCase(),
|
|
674
|
+
operation,
|
|
675
|
+
path,
|
|
676
|
+
profile: profileName,
|
|
677
|
+
schemaMeta: metaSpec.needsSchemas ? makeSchemaMetaLookup(handler) : undefined,
|
|
678
|
+
spec: metaSpec,
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
if (requestBody) operation.requestBody = methods.length > 1 ? cloneJson(requestBody) : requestBody
|
|
682
|
+
if (parameters.length > 0) operation.parameters = methods.length > 1 ? cloneJson(parameters) : parameters
|
|
683
|
+
operation.responses = methods.length > 1 ? cloneJson(responses) : responses
|
|
684
|
+
|
|
685
|
+
const pathItem = paths[oaPath] ?? (paths[oaPath] = {})
|
|
686
|
+
pathItem[methodKey] = operation
|
|
687
|
+
operations.push({
|
|
688
|
+
handler,
|
|
689
|
+
method: methodKey,
|
|
690
|
+
source: handler.fn ?? handler,
|
|
691
|
+
operation,
|
|
692
|
+
params: params.slice(shortestParams.get(handler) ?? 0).map((p) => p.name as string),
|
|
693
|
+
path: oaPath,
|
|
694
|
+
})
|
|
695
|
+
emitted.push({ meta, method: methodKey, operation, path: oaPath })
|
|
696
|
+
}
|
|
533
697
|
}
|
|
534
698
|
|
|
535
|
-
const wsRoutes
|
|
536
|
-
walkWSRoutes(tree, "", wsRoutes)
|
|
699
|
+
const wsRoutes = collectWsRoutes(app)
|
|
537
700
|
|
|
538
701
|
for (const { handler, path } of wsRoutes) {
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
541
|
-
|
|
702
|
+
const meta = handler.mt
|
|
703
|
+
if (isMetaInternal(meta)) continue
|
|
704
|
+
if (routeFilter && !routeFilter({ meta: (meta ?? {}) as TMeta, method: "WS", path })) continue
|
|
705
|
+
|
|
706
|
+
const oaPath = publicPath(toOpenApiPath(path), settings)
|
|
707
|
+
if (paths[oaPath]?.get !== undefined) {
|
|
708
|
+
collector.add(
|
|
709
|
+
"WS_SHADOWED",
|
|
710
|
+
"warn",
|
|
711
|
+
`WS ${path}: GET ${oaPath} is an HTTP operation; the websocket route is left out of the document`,
|
|
712
|
+
)
|
|
713
|
+
continue
|
|
542
714
|
}
|
|
543
715
|
|
|
544
716
|
const operation: Record<string, unknown> = { "x-websocket": true }
|
|
545
|
-
const parameters: Array<Record<string, unknown>> =
|
|
717
|
+
const parameters: Array<Record<string, unknown>> = pathParameters(path)
|
|
546
718
|
|
|
547
719
|
applyMetaSpec({
|
|
548
720
|
collector,
|
|
549
721
|
filter: profileFilter,
|
|
550
722
|
kind: "ws",
|
|
551
|
-
meta
|
|
723
|
+
meta,
|
|
552
724
|
method: "WS",
|
|
553
725
|
operation,
|
|
554
726
|
path,
|
|
@@ -557,58 +729,165 @@ export function generateOpenApiFromTree<TMeta = unknown>(
|
|
|
557
729
|
spec: metaSpec,
|
|
558
730
|
})
|
|
559
731
|
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
required: true,
|
|
566
|
-
schema: { type: "string" },
|
|
567
|
-
})
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
if (handler.iv?.search) {
|
|
571
|
-
const searchEntry = handler.iv.search
|
|
572
|
-
const searchSchema =
|
|
573
|
-
"_tag" in searchEntry
|
|
574
|
-
? (searchEntry as { schema: StandardSchemaLike }).schema
|
|
575
|
-
: (searchEntry as StandardSchemaLike)
|
|
576
|
-
const jsonSchema = asJsonSchema(searchSchema as StandardSchemaLike | Record<string, unknown>, "input")
|
|
577
|
-
if (jsonSchema && typeof jsonSchema === "object") {
|
|
578
|
-
const props = jsonSchema.properties as Record<string, unknown> | undefined
|
|
579
|
-
const required = new Set((jsonSchema.required ?? []) as string[])
|
|
580
|
-
if (props) {
|
|
581
|
-
for (const [name, schema] of Object.entries(props)) {
|
|
582
|
-
parameters.push({
|
|
583
|
-
in: "query",
|
|
584
|
-
name,
|
|
585
|
-
required: required.has(name),
|
|
586
|
-
schema,
|
|
587
|
-
})
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
732
|
+
const searchEntry = handler.iv?.search
|
|
733
|
+
if (searchEntry) {
|
|
734
|
+
const jsonSchema = asJsonSchema(unwrapEntry(searchEntry as InputSchemaEntry), "input")
|
|
735
|
+
const hint = deriveSchemaName({ method: "get", path: oaPath, role: "request" })
|
|
736
|
+
parameters.push(...propertyParameters(jsonSchema, "query", hoister, hint))
|
|
591
737
|
}
|
|
592
738
|
|
|
593
739
|
if (parameters.length > 0) operation.parameters = parameters
|
|
594
740
|
operation.responses = { "101": { description: "WebSocket upgrade" } }
|
|
595
741
|
|
|
596
|
-
paths[oaPath]
|
|
742
|
+
const pathItem = paths[oaPath] ?? (paths[oaPath] = {})
|
|
743
|
+
pathItem.get = operation
|
|
744
|
+
operations.push({ handler, method: "get", operation, params: [], path: oaPath, source: handler.fn ?? handler })
|
|
597
745
|
}
|
|
598
746
|
|
|
747
|
+
assignOperationIds(operations, collector)
|
|
748
|
+
checkSecurityReferences(operations, options.securitySchemes, collector)
|
|
749
|
+
if (invalidateLevel(options.invalidate) !== "off") {
|
|
750
|
+
const inventory = new Set(collected.map((r) => `${r.method} ${r.path}`))
|
|
751
|
+
for (const bad of findInvalidSelectors(emitted, inventory)) {
|
|
752
|
+
collector.add(
|
|
753
|
+
"INVALID_SELECTOR",
|
|
754
|
+
"error",
|
|
755
|
+
`${bad.method} ${bad.path}: invalidate selector ${JSON.stringify(bad.selector)} names no route ` +
|
|
756
|
+
'— write "<METHOD> <pattern>" for a registered route, e.g. "GET /users/:id"',
|
|
757
|
+
)
|
|
758
|
+
}
|
|
759
|
+
}
|
|
599
760
|
collector.flush()
|
|
600
761
|
reportMissingInvalidate(emitted, options.invalidate)
|
|
601
762
|
|
|
602
763
|
const result: OpenApiSpec = {
|
|
603
|
-
info: options.info,
|
|
764
|
+
info: cleanInfo(options.info),
|
|
604
765
|
openapi: "3.1.0",
|
|
605
|
-
paths,
|
|
766
|
+
paths: { ...paths },
|
|
606
767
|
}
|
|
607
|
-
if (
|
|
608
|
-
result.
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
768
|
+
if (settings.stripPrefix) {
|
|
769
|
+
result.servers = [{ url: settings.stripPrefix }]
|
|
770
|
+
}
|
|
771
|
+
const hoisted = Object.keys(hoister.components).length > 0 ? { ...hoister.components } : null
|
|
772
|
+
if (hoisted || options.securitySchemes) {
|
|
773
|
+
result.components = {}
|
|
774
|
+
if (hoisted) result.components.schemas = hoisted
|
|
775
|
+
if (options.securitySchemes) result.components.securitySchemes = options.securitySchemes
|
|
612
776
|
}
|
|
613
777
|
return result
|
|
614
778
|
}
|
|
779
|
+
|
|
780
|
+
/** Only the Info Object's own fields: callers pass their whole options bag. */
|
|
781
|
+
function cleanInfo(info: OpenApiInfo): OpenApiInfo {
|
|
782
|
+
const out: OpenApiInfo = { title: info.title, version: info.version }
|
|
783
|
+
if (typeof info.description === "string") out.description = info.description
|
|
784
|
+
return out
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/** One parameter per property of an object schema (`search`, `headers`, `cookies`). */
|
|
788
|
+
function propertyParameters(
|
|
789
|
+
jsonSchema: Record<string, unknown>,
|
|
790
|
+
location: "cookie" | "header" | "query",
|
|
791
|
+
hoister: LocalRefHoister,
|
|
792
|
+
hint: string,
|
|
793
|
+
): Array<Record<string, unknown>> {
|
|
794
|
+
const out: Array<Record<string, unknown>> = []
|
|
795
|
+
const hoisted = hoister.hoist(jsonSchema, hint)
|
|
796
|
+
/* a recursive search schema hoists its root; its properties are still the parameters */
|
|
797
|
+
const root = hoisted.$ref === undefined ? hoisted : jsonSchema
|
|
798
|
+
const props = root.properties as Record<string, unknown> | undefined
|
|
799
|
+
if (!props) return out
|
|
800
|
+
const required = new Set((root.required as string[] | undefined) ?? [])
|
|
801
|
+
for (const name of Object.keys(props)) {
|
|
802
|
+
const propSchema = props[name] as Record<string, unknown> | undefined
|
|
803
|
+
if (propSchema?.["x-internal"] === true) continue
|
|
804
|
+
out.push({ in: location, name, required: required.has(name), schema: propSchema ?? {} })
|
|
805
|
+
}
|
|
806
|
+
return out
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
type ErrorResponseContext = {
|
|
810
|
+
baseErrorJsonSchema: Record<string, unknown>
|
|
811
|
+
customErrorAddsSchema: Record<string, unknown> | null
|
|
812
|
+
errorMeta: Record<string, ErrorMetaEntry> | null
|
|
813
|
+
factory: Record<string, () => HoneyError> | null
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function addErrorResponses(
|
|
817
|
+
responses: Record<string, Record<string, unknown>>,
|
|
818
|
+
handler: { ek: Set<string> },
|
|
819
|
+
ctx: ErrorResponseContext,
|
|
820
|
+
): void {
|
|
821
|
+
type ErrorEntry = { key: string; schema: Record<string, unknown> | null }
|
|
822
|
+
const byStatus = new Map<number, ErrorEntry[]>()
|
|
823
|
+
for (const ek of handler.ek) {
|
|
824
|
+
const info = resolveErrorInfo(ek, ctx.factory)
|
|
825
|
+
if (info.status <= 0) continue
|
|
826
|
+
let entries = byStatus.get(info.status)
|
|
827
|
+
if (!entries) {
|
|
828
|
+
entries = []
|
|
829
|
+
byStatus.set(info.status, entries)
|
|
830
|
+
}
|
|
831
|
+
const meta = ctx.errorMeta?.[ek]
|
|
832
|
+
let customSchema: Record<string, unknown> | null = null
|
|
833
|
+
if (meta?.schema) {
|
|
834
|
+
const converted = asJsonSchema(meta.schema as StandardSchemaLike)
|
|
835
|
+
if (converted && Object.keys(converted).length > 0) {
|
|
836
|
+
customSchema = ctx.customErrorAddsSchema ? { allOf: [converted, ctx.customErrorAddsSchema] } : converted
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
entries.push({ key: ek, schema: customSchema })
|
|
840
|
+
}
|
|
841
|
+
const standardSchema = (status: number, keys: string[]): Record<string, unknown> => {
|
|
842
|
+
const schema = cloneJson(ctx.baseErrorJsonSchema)
|
|
843
|
+
const props = schema.properties as Record<string, unknown> | undefined
|
|
844
|
+
if (props?.error_key) props.error_key = { enum: [...keys].sort(), type: "string" }
|
|
845
|
+
if (props?.status) props.status = { enum: [status], type: "integer" }
|
|
846
|
+
return schema
|
|
847
|
+
}
|
|
848
|
+
for (const [status, entries] of byStatus) {
|
|
849
|
+
if (responses[String(status)] !== undefined) continue
|
|
850
|
+
const standardKeys = entries.filter((e) => !e.schema).map((e) => e.key)
|
|
851
|
+
const customSchemas = entries.filter((e) => e.schema).map((e) => e.schema as Record<string, unknown>)
|
|
852
|
+
|
|
853
|
+
let schema: Record<string, unknown>
|
|
854
|
+
if (customSchemas.length === 0) {
|
|
855
|
+
schema = standardSchema(status, standardKeys)
|
|
856
|
+
} else if (standardKeys.length === 0 && customSchemas.length === 1) {
|
|
857
|
+
schema = customSchemas[0]
|
|
858
|
+
} else {
|
|
859
|
+
const schemas: Record<string, unknown>[] = []
|
|
860
|
+
if (standardKeys.length > 0) schemas.push(standardSchema(status, standardKeys))
|
|
861
|
+
schemas.push(...customSchemas)
|
|
862
|
+
schema = { oneOf: schemas }
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
responses[String(status)] = {
|
|
866
|
+
content: { "application/json": { schema } },
|
|
867
|
+
description: entries
|
|
868
|
+
.map((e) => e.key)
|
|
869
|
+
.sort()
|
|
870
|
+
.join(", "),
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/** Every scheme an operation's `security` names must exist in `components.securitySchemes`. */
|
|
876
|
+
function checkSecurityReferences(
|
|
877
|
+
operations: readonly EmittedOperation[],
|
|
878
|
+
schemes: Record<string, unknown> | undefined,
|
|
879
|
+
collector: MetaSpecCollector,
|
|
880
|
+
): void {
|
|
881
|
+
for (const { method, operation, path } of operations) {
|
|
882
|
+
for (const name of securitySchemeNames(operation.security)) {
|
|
883
|
+
if (schemes && Object.hasOwn(schemes, name)) continue
|
|
884
|
+
collector.add(
|
|
885
|
+
"UNKNOWN_SECURITY_SCHEME",
|
|
886
|
+
schemes ? "error" : "warn",
|
|
887
|
+
schemes
|
|
888
|
+
? `${method.toUpperCase()} ${path}: security names "${name}", which is not in securitySchemes`
|
|
889
|
+
: `security scheme "${name}" (first used by ${method.toUpperCase()} ${path}) is not declared — pass securitySchemes`,
|
|
890
|
+
)
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|