@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/types.ts
CHANGED
|
@@ -136,10 +136,13 @@ export const codeToStatusKey: Record<number, StatusKey> = Object.fromEntries(
|
|
|
136
136
|
/** Framework error keys — centralized to prevent typo-induced silent failures */
|
|
137
137
|
export const EK = {
|
|
138
138
|
bad_gateway: "bad_gateway",
|
|
139
|
+
bad_request: "bad_request",
|
|
139
140
|
content_too_large: "content_too_large",
|
|
140
141
|
forbidden: "forbidden",
|
|
141
142
|
gateway_timeout: "gateway_timeout",
|
|
142
143
|
internal_server_error: "internal_server_error",
|
|
144
|
+
/** a request body the declared parser cannot read (bad JSON, broken multipart) — 400 */
|
|
145
|
+
malformed_body: "malformed_body",
|
|
143
146
|
method_not_allowed: "method_not_allowed",
|
|
144
147
|
not_found: "not_found",
|
|
145
148
|
output_content_type_mismatch: "output_content_type_mismatch",
|
|
@@ -726,6 +729,11 @@ export type InferBasePath<T> = T extends { readonly $basePath: infer P } ? P : n
|
|
|
726
729
|
/** Merge two path segments — "/" is identity */
|
|
727
730
|
export type MergePath<A extends string, B extends string> = A extends "/" ? B : B extends "/" ? A : `${A}${B}`
|
|
728
731
|
|
|
732
|
+
/** The routes of a mounted sub-app, moved under the mounting handle's basePath. */
|
|
733
|
+
export type PrefixRoutes<TBase extends string, TRoutes> = TBase extends "/"
|
|
734
|
+
? TRoutes
|
|
735
|
+
: { [P in keyof TRoutes & string as MergePath<TBase, P>]: TRoutes[P] }
|
|
736
|
+
|
|
729
737
|
/* ── Tap types ── */
|
|
730
738
|
|
|
731
739
|
/** Queued side-effect emitted by c.tap() inside a handler */
|
package/src/validation.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { HoneyError } from "./error.ts"
|
|
2
|
+
import { parseCookieHeader } from "./cookie.ts"
|
|
3
|
+
import { dict } from "./dict.ts"
|
|
4
|
+
import { bodyParserFor, type BodyParser } from "./media-type.ts"
|
|
2
5
|
import type {
|
|
3
6
|
FieldError,
|
|
4
7
|
InputSchemaEntry,
|
|
@@ -36,20 +39,33 @@ export function mapNormalizedCode(code: string): string {
|
|
|
36
39
|
return CODE_MAP[code] ?? "field_invalid"
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
/** `bodyParserFor` with both form encodings folded into `"form"`. */
|
|
39
43
|
export function selectParser(contentType: string | null): "form" | "json" | null {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (contentType.startsWith("multipart/form-data")) return "form"
|
|
43
|
-
if (contentType.startsWith("application/x-www-form-urlencoded")) return "form"
|
|
44
|
-
return null
|
|
44
|
+
const parser = bodyParserFor(contentType)
|
|
45
|
+
return parser === null ? null : parser === "json" ? "json" : "form"
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Throws when a route for `method` declares a `json` or `form` schema it can never receive:
|
|
50
|
+
* GET and HEAD requests cannot carry a body (Fetch forbids one). Every other method, DELETE and
|
|
51
|
+
* OPTIONS included, has its declared body validated on every request.
|
|
52
|
+
*/
|
|
53
|
+
export function assertBodySchemaAllowed(iv: InputSchemasDef | null, method: string, path: string): void {
|
|
54
|
+
if (iv === null || (iv.json === undefined && iv.form === undefined)) return
|
|
55
|
+
if (method !== "GET" && method !== "HEAD") return
|
|
56
|
+
const kind = iv.json !== undefined ? "json" : "form"
|
|
57
|
+
throw new Error(
|
|
58
|
+
`${method} ${path} declares a ${kind} body schema, but ${method} requests have no body. ` +
|
|
59
|
+
`Validate query data with \`search\`, or register the route for a method that carries a body.`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Content-Type vs declared json/form — no body read. Applies to every method: a declared body
|
|
64
|
+
* is required, so a GET reaching an `all()` route with a body schema is a 415 too. */
|
|
48
65
|
export function assertRequestContentType(iv: InputSchemasDef, req: Request): void {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
const ok = (iv.json && parser === "json") || (iv.form && parser === "form")
|
|
66
|
+
if (iv.json || iv.form) {
|
|
67
|
+
const parser = bodyParserFor(req.headers.get("content-type"))
|
|
68
|
+
const ok = (iv.json && parser === "json") || (iv.form && (parser === "multipart" || parser === "urlencoded"))
|
|
53
69
|
if (!ok) {
|
|
54
70
|
throw new HoneyError({
|
|
55
71
|
errorKey: EK.unsupported_media_type,
|
|
@@ -59,33 +75,8 @@ export function assertRequestContentType(iv: InputSchemasDef, req: Request): voi
|
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return decodeURIComponent(value)
|
|
65
|
-
} catch {
|
|
66
|
-
return value
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function parseCookies(header: string): Record<string, string> {
|
|
71
|
-
const result: Record<string, string> = {}
|
|
72
|
-
if (header.length === 0) return result
|
|
73
|
-
for (const pair of header.split(";")) {
|
|
74
|
-
const trimmed = pair.trim()
|
|
75
|
-
const eqIdx = trimmed.indexOf("=")
|
|
76
|
-
if (eqIdx === -1) continue
|
|
77
|
-
const name = trimmed.slice(0, eqIdx)
|
|
78
|
-
if (DANGEROUS_KEYS.has(name)) continue
|
|
79
|
-
let value = trimmed.slice(eqIdx + 1)
|
|
80
|
-
if (value.indexOf("%") !== -1) value = tryDecodeCookieValue(value)
|
|
81
|
-
/* RFC 6265: strip surrounding double quotes */
|
|
82
|
-
if (value.length >= 2 && value.charCodeAt(0) === 34 && value.charCodeAt(value.length - 1) === 34) {
|
|
83
|
-
value = value.slice(1, -1)
|
|
84
|
-
}
|
|
85
|
-
result[name] = value
|
|
86
|
-
}
|
|
87
|
-
return result
|
|
88
|
-
}
|
|
78
|
+
/** Cookie parsing lives with the serializer so the two round-trip; see `cookie.ts`. */
|
|
79
|
+
export const parseCookies: (header: string) => Record<string, string> = parseCookieHeader
|
|
89
80
|
|
|
90
81
|
function toPropertyKey(segment: PropertyKey | { readonly key: PropertyKey }): PropertyKey {
|
|
91
82
|
if (typeof segment === "object" && segment !== null && "key" in segment) {
|
|
@@ -122,7 +113,8 @@ export function normalizeIssues(issues: ReadonlyArray<StandardSchemaIssue>, vend
|
|
|
122
113
|
}
|
|
123
114
|
|
|
124
115
|
export function issuesToFieldErrors(issues: NormalizedIssue[], prefix: string): Record<string, FieldError[]> {
|
|
125
|
-
|
|
116
|
+
/* field names come from the request (record keys): `toString` must not find a prototype method */
|
|
117
|
+
const fields = dict<FieldError[]>()
|
|
126
118
|
for (const issue of issues) {
|
|
127
119
|
const fieldName = issue.path.at(-1)?.toString() ?? "unknown"
|
|
128
120
|
const fullPath = `${prefix}.${issue.path.map(String).join(".")}`
|
|
@@ -139,38 +131,162 @@ export function issuesToFieldErrors(issues: NormalizedIssue[], prefix: string):
|
|
|
139
131
|
return fields
|
|
140
132
|
}
|
|
141
133
|
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
134
|
+
function failValidation(schema: StandardSchemaLike, issues: ReadonlyArray<StandardSchemaIssue>, prefix: string): never {
|
|
135
|
+
const normalized = normalizeIssues(issues, schema["~standard"].vendor)
|
|
136
|
+
throw new HoneyError({
|
|
137
|
+
errorKey: EK.validation_failed,
|
|
138
|
+
fields: issuesToFieldErrors(normalized, prefix),
|
|
139
|
+
status: SK.bad_request,
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
type StandardResult = { issues?: ReadonlyArray<StandardSchemaIssue>; value?: unknown }
|
|
144
|
+
|
|
145
|
+
/* Most validators answer synchronously; only a schema that returns a promise costs an await. */
|
|
146
|
+
function runSchema(schema: StandardSchemaLike, data: unknown, prefix: string): unknown {
|
|
147
|
+
const result = schema["~standard"].validate(data) as StandardResult | Promise<StandardResult>
|
|
148
|
+
if (result instanceof Promise) {
|
|
149
|
+
return result.then((r) => (r.issues ? failValidation(schema, r.issues, prefix) : r.value))
|
|
152
150
|
}
|
|
153
|
-
return result.value
|
|
151
|
+
return result.issues ? failValidation(schema, result.issues, prefix) : result.value
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* Duplicate keys in search and form data. One policy for both:
|
|
156
|
+
*
|
|
157
|
+
* - A property the schema types as an array always gets an array, even for a single value
|
|
158
|
+
* (`?tag=a` → `["a"]`, one uploaded file → `[File]`).
|
|
159
|
+
* - A property the schema types as a scalar gets the FIRST value, like `ctx.search` and
|
|
160
|
+
* cookies (`role=user&role=admin` → `"user"`), so every layer that reads the key agrees.
|
|
161
|
+
* - Anything else (a union of scalar and array, an untyped or unknown key, or a schema that
|
|
162
|
+
* cannot describe itself) gets a scalar for one value and an array for repeated values.
|
|
163
|
+
*
|
|
164
|
+
* Shapes come from the schema's Standard JSON Schema (`~standard.jsonSchema`, zod ≥ 4.2 and
|
|
165
|
+
* arktype); validators without it (valibot, yup, effect) get the last rule for every key.
|
|
166
|
+
* Cookies are always first-wins (RFC 6265 sends the most specific cookie first); headers are
|
|
167
|
+
* joined with `, ` as Fetch does.
|
|
168
|
+
*/
|
|
169
|
+
type KeyShape = "array" | "natural" | "scalar"
|
|
170
|
+
type Shape = Map<string, KeyShape>
|
|
171
|
+
|
|
172
|
+
const shapeCache = new WeakMap<object, Shape | null>()
|
|
173
|
+
|
|
174
|
+
type JsonSchemaNode = {
|
|
175
|
+
$ref?: string
|
|
176
|
+
allOf?: JsonSchemaNode[]
|
|
177
|
+
anyOf?: JsonSchemaNode[]
|
|
178
|
+
oneOf?: JsonSchemaNode[]
|
|
179
|
+
properties?: Record<string, JsonSchemaNode>
|
|
180
|
+
type?: string | string[]
|
|
181
|
+
$defs?: Record<string, JsonSchemaNode>
|
|
182
|
+
definitions?: Record<string, JsonSchemaNode>
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function deref(node: JsonSchemaNode, root: JsonSchemaNode): JsonSchemaNode {
|
|
186
|
+
const ref = node.$ref
|
|
187
|
+
if (typeof ref !== "string") return node
|
|
188
|
+
const m = /^#\/(\$defs|definitions)\/(.+)$/.exec(ref)
|
|
189
|
+
if (m === null) return node
|
|
190
|
+
const defs = m[1] === "$defs" ? root.$defs : root.definitions
|
|
191
|
+
const target = defs !== undefined && Object.hasOwn(defs, m[2]!) ? defs[m[2]!] : undefined
|
|
192
|
+
return target ?? node
|
|
154
193
|
}
|
|
155
194
|
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
195
|
+
function classify(node: JsonSchemaNode, root: JsonSchemaNode, depth = 0): KeyShape {
|
|
196
|
+
if (depth > 8) return "natural"
|
|
197
|
+
const n = deref(node, root)
|
|
198
|
+
const t = n.type
|
|
199
|
+
if (typeof t === "string") return t === "array" ? "array" : "scalar"
|
|
200
|
+
if (Array.isArray(t)) {
|
|
201
|
+
const nonNull = t.filter((x) => x !== "null")
|
|
202
|
+
if (nonNull.length === 0) return "natural"
|
|
203
|
+
if (nonNull.every((x) => x === "array")) return "array"
|
|
204
|
+
return nonNull.includes("array") ? "natural" : "scalar"
|
|
205
|
+
}
|
|
206
|
+
const branches = n.anyOf ?? n.oneOf
|
|
207
|
+
if (branches !== undefined && branches.length > 0) {
|
|
208
|
+
const kinds = branches.filter((b) => deref(b, root).type !== "null").map((b) => classify(b, root, depth + 1))
|
|
209
|
+
if (kinds.length === 0) return "natural"
|
|
210
|
+
if (kinds.every((k) => k === "array")) return "array"
|
|
211
|
+
if (kinds.every((k) => k === "scalar")) return "scalar"
|
|
212
|
+
return "natural"
|
|
213
|
+
}
|
|
214
|
+
if (n.allOf !== undefined && n.allOf.length > 0) {
|
|
215
|
+
const kinds = n.allOf.map((b) => classify(b, root, depth + 1))
|
|
216
|
+
if (kinds.includes("array")) return "array"
|
|
217
|
+
if (kinds.includes("scalar")) return "scalar"
|
|
218
|
+
}
|
|
219
|
+
return "natural"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function collectProperties(node: JsonSchemaNode, root: JsonSchemaNode, out: Shape, depth = 0): void {
|
|
223
|
+
if (depth > 8) return
|
|
224
|
+
const n = deref(node, root)
|
|
225
|
+
if (n.properties !== undefined) {
|
|
226
|
+
for (const key of Object.keys(n.properties)) {
|
|
227
|
+
if (!out.has(key)) out.set(key, classify(n.properties[key]!, root))
|
|
167
228
|
}
|
|
168
229
|
}
|
|
169
|
-
|
|
230
|
+
for (const part of n.allOf ?? []) collectProperties(part, root, out, depth + 1)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
type StandardJsonSchemaLike = {
|
|
234
|
+
input?: (opts: { libraryOptions?: Record<string, unknown>; target: string }) => unknown
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function shapeOf(schema: StandardSchemaLike): Shape | null {
|
|
238
|
+
const cached = shapeCache.get(schema)
|
|
239
|
+
if (cached !== undefined) return cached
|
|
240
|
+
let shape: Shape | null = null
|
|
241
|
+
const js = (schema["~standard"] as { jsonSchema?: StandardJsonSchemaLike }).jsonSchema
|
|
242
|
+
if (js !== undefined && typeof js.input === "function") {
|
|
243
|
+
try {
|
|
244
|
+
const root = js.input({ libraryOptions: { unrepresentable: "any" }, target: "draft-2020-12" }) as JsonSchemaNode
|
|
245
|
+
if (root !== null && typeof root === "object") {
|
|
246
|
+
shape = new Map()
|
|
247
|
+
collectProperties(root, root, shape)
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
/* a schema that cannot describe itself falls back to the natural shape */
|
|
251
|
+
shape = null
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
shapeCache.set(schema, shape)
|
|
255
|
+
return shape
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Applies the duplicate-key policy to `key → values` collected in request order. */
|
|
259
|
+
function shapeRecord<T>(all: Map<string, T[]>, shape: Shape | null): Record<string, T | T[]> {
|
|
260
|
+
const out = dict<T | T[]>()
|
|
261
|
+
for (const [key, values] of all) {
|
|
262
|
+
const kind = shape?.get(key) ?? "natural"
|
|
263
|
+
out[key] = kind === "array" ? values : kind === "scalar" || values.length === 1 ? values[0]! : values
|
|
264
|
+
}
|
|
265
|
+
return out
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function collect<T>(entries: Iterable<[string, T]>, skipDangerous: boolean): Map<string, T[]> {
|
|
269
|
+
const all = new Map<string, T[]>()
|
|
270
|
+
for (const [key, value] of entries) {
|
|
271
|
+
if (skipDangerous && DANGEROUS_KEYS.has(key)) continue
|
|
272
|
+
const list = all.get(key)
|
|
273
|
+
if (list === undefined) all.set(key, [value])
|
|
274
|
+
else list.push(value)
|
|
275
|
+
}
|
|
276
|
+
return all
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function searchEntries(req: Request, searchAll: Record<string, string[]> | undefined): Iterable<[string, string]> {
|
|
280
|
+
if (searchAll === undefined) return new URL(req.url).searchParams
|
|
281
|
+
const entries: [string, string][] = []
|
|
282
|
+
for (const key of Object.keys(searchAll)) {
|
|
283
|
+
for (const value of searchAll[key]!) entries.push([key, value])
|
|
284
|
+
}
|
|
285
|
+
return entries
|
|
170
286
|
}
|
|
171
287
|
|
|
172
288
|
function headersToRecord(headers: Headers): Record<string, string> {
|
|
173
|
-
const result
|
|
289
|
+
const result = dict<string>()
|
|
174
290
|
headers.forEach((value, key) => {
|
|
175
291
|
result[key] = value
|
|
176
292
|
})
|
|
@@ -192,29 +308,43 @@ function resolveSchema(entry: InputSchemaEntry): ResolvedSchema {
|
|
|
192
308
|
return { mode: "standard", schema: entry }
|
|
193
309
|
}
|
|
194
310
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
})
|
|
201
|
-
return record
|
|
311
|
+
/* A body the declared parser cannot read is the client's fault: 400 `malformed_body`, never a
|
|
312
|
+
* 500 through `onError`. Errors from reading the stream itself (a `bodyLimit` 413, a client
|
|
313
|
+
* abort) are not parse errors and pass through unchanged. */
|
|
314
|
+
function malformedBody(format: BodyParser, cause: unknown): HoneyError {
|
|
315
|
+
return new HoneyError({ cause, errorKey: EK.malformed_body, status: SK.bad_request, vars: { format } })
|
|
202
316
|
}
|
|
203
317
|
|
|
204
|
-
function
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
318
|
+
async function readJson(req: Request): Promise<unknown> {
|
|
319
|
+
const text = await req.text()
|
|
320
|
+
try {
|
|
321
|
+
return JSON.parse(text) as unknown
|
|
322
|
+
} catch (err) {
|
|
323
|
+
throw malformedBody("json", err)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function readForm(req: Request, parser: "multipart" | "urlencoded"): Promise<Iterable<[string, unknown]>> {
|
|
328
|
+
if (parser === "urlencoded") return new URLSearchParams(await req.text())
|
|
329
|
+
const bytes = await req.arrayBuffer()
|
|
330
|
+
try {
|
|
331
|
+
const contentType = req.headers.get("content-type") ?? ""
|
|
332
|
+
return (await new Response(bytes, { headers: { "content-type": contentType } }).formData()) as unknown as Iterable<
|
|
333
|
+
[string, unknown]
|
|
334
|
+
>
|
|
335
|
+
} catch (err) {
|
|
336
|
+
throw malformedBody("multipart", err)
|
|
337
|
+
}
|
|
212
338
|
}
|
|
213
339
|
|
|
340
|
+
/** What validation reads from the request context when it has one; `ctx.searchAll` is cached. */
|
|
341
|
+
type ValidationSource = { readonly searchAll?: Record<string, string[]> }
|
|
342
|
+
|
|
214
343
|
export async function validateInput(
|
|
215
344
|
schemas: InputSchemasDef,
|
|
216
345
|
req: Request,
|
|
217
346
|
params: Record<string, string>,
|
|
347
|
+
source?: ValidationSource,
|
|
218
348
|
): Promise<ValidatedInput> {
|
|
219
349
|
const result: ValidatedInput = {}
|
|
220
350
|
|
|
@@ -227,7 +357,8 @@ export async function validateInput(
|
|
|
227
357
|
/* search */
|
|
228
358
|
if (schemas.search) {
|
|
229
359
|
const { mode, schema } = resolveSchema(schemas.search)
|
|
230
|
-
const
|
|
360
|
+
const all = collect(searchEntries(req, source?.searchAll), false)
|
|
361
|
+
const parsed = shapeRecord(all, mode === "readableStream" ? null : shapeOf(schema))
|
|
231
362
|
result.search = mode === "readableStream" ? parsed : await runSchema(schema, parsed, "search")
|
|
232
363
|
}
|
|
233
364
|
|
|
@@ -246,29 +377,23 @@ export async function validateInput(
|
|
|
246
377
|
result.cookies = mode === "readableStream" ? parsed : await runSchema(schema, parsed, "cookies")
|
|
247
378
|
}
|
|
248
379
|
|
|
249
|
-
/* body
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
const parser = selectParser(contentType)
|
|
380
|
+
/* body: json or form, for every method that reaches here (GET/HEAD body schemas are
|
|
381
|
+
* rejected at registration) */
|
|
382
|
+
if (schemas.json || schemas.form) {
|
|
383
|
+
assertRequestContentType(schemas, req)
|
|
384
|
+
const parser = bodyParserFor(req.headers.get("content-type"))
|
|
255
385
|
|
|
256
386
|
if (schemas.json && parser === "json") {
|
|
257
387
|
const { mode, schema } = resolveSchema(schemas.json)
|
|
258
|
-
if (mode
|
|
259
|
-
|
|
260
|
-
} else {
|
|
261
|
-
const body: unknown = await req.json()
|
|
262
|
-
result.json = await runSchema(schema, body, "json")
|
|
388
|
+
if (mode !== "readableStream") {
|
|
389
|
+
result.json = await runSchema(schema, await readJson(req), "json")
|
|
263
390
|
}
|
|
264
|
-
|
|
391
|
+
/* readableStream: body untouched — the handler reads ctx.req.body directly */
|
|
392
|
+
} else if (schemas.form && (parser === "multipart" || parser === "urlencoded")) {
|
|
265
393
|
const { mode, schema } = resolveSchema(schemas.form)
|
|
266
|
-
if (mode
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
result.form = await runSchema(schema, formDataToRecord(await req.formData()), "form")
|
|
270
|
-
} else {
|
|
271
|
-
result.form = await runSchema(schema, urlEncodedToRecord(await req.text()), "form")
|
|
394
|
+
if (mode !== "readableStream") {
|
|
395
|
+
const record = shapeRecord(collect(await readForm(req, parser), true), shapeOf(schema))
|
|
396
|
+
result.form = await runSchema(schema, record, "form")
|
|
272
397
|
}
|
|
273
398
|
}
|
|
274
399
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isHoneyResponse } from "./honey-response.ts"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apply header edits to a response a middleware got back from `next()`.
|
|
5
|
+
*
|
|
6
|
+
* - `101 Switching Protocols` passes through untouched. Upgrade responses are
|
|
7
|
+
* adapter sentinels (Node and Bun fake the status, Workers carry `webSocket`),
|
|
8
|
+
* and rebuilding one throws or drops the socket.
|
|
9
|
+
* - Honey's own responses and `new Response()` objects are edited in place.
|
|
10
|
+
* - Responses with immutable headers (`fetch()`, `Response.redirect()`,
|
|
11
|
+
* `cache.match()`) are copied first: same body stream, status, status text
|
|
12
|
+
* and headers, then edited.
|
|
13
|
+
*
|
|
14
|
+
* `edit` must not read the body.
|
|
15
|
+
*/
|
|
16
|
+
export function withHeaders(response: Response, edit: (headers: Headers) => void): Response {
|
|
17
|
+
if (response.status === 101) return response
|
|
18
|
+
if (isHoneyResponse(response)) {
|
|
19
|
+
edit(response.headers as unknown as Headers)
|
|
20
|
+
return response
|
|
21
|
+
}
|
|
22
|
+
if (!headersAreImmutable(response)) {
|
|
23
|
+
edit(response.headers)
|
|
24
|
+
return response
|
|
25
|
+
}
|
|
26
|
+
const copy = new Response(response.body, {
|
|
27
|
+
headers: new Headers(response.headers),
|
|
28
|
+
status: response.status,
|
|
29
|
+
statusText: response.statusText,
|
|
30
|
+
})
|
|
31
|
+
edit(copy.headers)
|
|
32
|
+
return copy
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Probe with a header name no one sends: immutable Headers throw on any write. */
|
|
36
|
+
const PROBE = "x-honey-mutability-probe"
|
|
37
|
+
|
|
38
|
+
function headersAreImmutable(response: Response): boolean {
|
|
39
|
+
try {
|
|
40
|
+
response.headers.delete(PROBE)
|
|
41
|
+
return false
|
|
42
|
+
} catch {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/ws/bun.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
type RawSocket,
|
|
3
|
+
type WSAdapter,
|
|
4
|
+
type WSAdapterOptions,
|
|
5
|
+
type WSContext,
|
|
6
|
+
WSContextImpl,
|
|
7
|
+
type WSHandler,
|
|
8
|
+
} from "./cloudflare.ts"
|
|
2
9
|
|
|
3
10
|
import type { WSAdapter, WSContext, WSHandler } from "./cloudflare.ts"
|
|
4
|
-
import { WSContextImpl } from "./cloudflare.ts"
|
|
11
|
+
import { fireHandler, WSContextImpl } from "./cloudflare.ts"
|
|
12
|
+
import { resolveWSOptions, selectProtocol, toArrayBuffer, type WSAdapterOptions } from "./shared.ts"
|
|
5
13
|
|
|
6
14
|
type BunRawSocket = {
|
|
7
15
|
close(code?: number, reason?: string): void
|
|
8
16
|
data: BunWSData
|
|
17
|
+
getBufferedAmount?(): number
|
|
9
18
|
readyState: number
|
|
10
19
|
send(data: ArrayBufferLike | ArrayBufferView | string, compress?: boolean): number
|
|
11
20
|
}
|
|
@@ -16,12 +25,17 @@ type BunWSData = {
|
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
type BunServer = {
|
|
19
|
-
upgrade<T>(req: Request, opts?: { data?: T }): boolean
|
|
28
|
+
upgrade<T>(req: Request, opts?: { data?: T; headers?: HeadersInit }): boolean
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export type BunWSAdapter = WSAdapter & {
|
|
32
|
+
/** Pass to `Bun.serve({ websocket })`; honey's `serve()` does. */
|
|
23
33
|
websocket: {
|
|
34
|
+
backpressureLimit: number
|
|
24
35
|
close(ws: BunRawSocket, code: number, reason: string): void
|
|
36
|
+
closeOnBackpressureLimit: boolean
|
|
37
|
+
idleTimeout: number
|
|
38
|
+
maxPayloadLength: number
|
|
25
39
|
message(ws: BunRawSocket, data: ArrayBufferView | string): void
|
|
26
40
|
open(ws: BunRawSocket): void
|
|
27
41
|
}
|
|
@@ -30,12 +44,27 @@ export type BunWSAdapter = WSAdapter & {
|
|
|
30
44
|
/**
|
|
31
45
|
* Bun WebSocket adapter.
|
|
32
46
|
* Returns adapter + websocket handler object for Bun.serve().
|
|
33
|
-
* Bun has no onError callback (noted limitation).
|
|
34
|
-
*
|
|
35
|
-
* Use nodeWebSocket() for keepalive support.
|
|
47
|
+
* Bun has no onError callback (noted limitation). Bun pings idle sockets itself
|
|
48
|
+
* (`idleTimeout`, whole seconds, at most 960); there is no `keepalive` option.
|
|
36
49
|
*/
|
|
37
|
-
export function bunWebSocket(): BunWSAdapter {
|
|
50
|
+
export function bunWebSocket(opts?: WSAdapterOptions): BunWSAdapter {
|
|
51
|
+
const options = resolveWSOptions(opts)
|
|
52
|
+
const open = new Set<BunRawSocket>()
|
|
53
|
+
const socketOf = (ws: BunRawSocket): WSContext => {
|
|
54
|
+
if (!ws.data.socket) ws.data.socket = new WSContextImpl(ws, options)
|
|
55
|
+
return ws.data.socket
|
|
56
|
+
}
|
|
38
57
|
return {
|
|
58
|
+
closeAll(code: number, reason: string) {
|
|
59
|
+
for (const ws of open) {
|
|
60
|
+
try {
|
|
61
|
+
ws.close(code, reason)
|
|
62
|
+
} catch {
|
|
63
|
+
/* already closing */
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
options,
|
|
39
68
|
upgrade(req: Request, env: unknown, handler: WSHandler<unknown>) {
|
|
40
69
|
const server = (env as Record<string, unknown>).server as BunServer
|
|
41
70
|
/*
|
|
@@ -43,8 +72,10 @@ export function bunWebSocket(): BunWSAdapter {
|
|
|
43
72
|
* WSContext cast is safe: actual socket is assigned in open/message/close
|
|
44
73
|
* before any user code accesses it.
|
|
45
74
|
*/
|
|
75
|
+
const protocol = selectProtocol(req, options)
|
|
46
76
|
const success = server.upgrade<BunWSData>(req, {
|
|
47
77
|
data: { handler, socket: undefined as unknown as WSContext },
|
|
78
|
+
headers: protocol === null ? undefined : { "sec-websocket-protocol": protocol },
|
|
48
79
|
})
|
|
49
80
|
if (!success) {
|
|
50
81
|
return {
|
|
@@ -63,19 +94,26 @@ export function bunWebSocket(): BunWSAdapter {
|
|
|
63
94
|
},
|
|
64
95
|
|
|
65
96
|
websocket: {
|
|
97
|
+
/* honey applies its own policy on send (WSContext); Bun's limit only bounds its queue */
|
|
98
|
+
backpressureLimit: Math.max(options.backpressureLimit, 1),
|
|
66
99
|
close(ws: BunRawSocket, code: number, reason: string) {
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
open.delete(ws)
|
|
101
|
+
const socket = socketOf(ws)
|
|
102
|
+
fireHandler("close", () => ws.data.handler.onClose?.(undefined, socket, code, reason))
|
|
69
103
|
},
|
|
104
|
+
closeOnBackpressureLimit: false,
|
|
105
|
+
idleTimeout: options.idleTimeout === 0 ? 0 : Math.min(Math.max(Math.ceil(options.idleTimeout / 1000), 1), 960),
|
|
106
|
+
maxPayloadLength: options.maxPayload,
|
|
70
107
|
message(ws: BunRawSocket, data: ArrayBufferView | string) {
|
|
71
|
-
|
|
72
|
-
/* Bun passes Buffer
|
|
73
|
-
const normalized = typeof data === "string" ? data : (
|
|
74
|
-
ws.data.handler.onMessage?.(undefined,
|
|
108
|
+
const socket = socketOf(ws)
|
|
109
|
+
/* Bun passes a Buffer for binary frames: hand over exactly its bytes, as an ArrayBuffer */
|
|
110
|
+
const normalized = typeof data === "string" ? data : toArrayBuffer(data)
|
|
111
|
+
fireHandler("message", () => ws.data.handler.onMessage?.(undefined, socket, normalized))
|
|
75
112
|
},
|
|
76
113
|
open(ws: BunRawSocket) {
|
|
77
|
-
|
|
78
|
-
|
|
114
|
+
open.add(ws)
|
|
115
|
+
const socket = socketOf(ws)
|
|
116
|
+
fireHandler("open", () => ws.data.handler.onOpen?.(undefined, socket))
|
|
79
117
|
},
|
|
80
118
|
},
|
|
81
119
|
}
|