@lovrozagar/honey 0.6.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +410 -149
- 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 +776 -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 +1893 -1761
- 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 +271 -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 +562 -424
- 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/node.ts
CHANGED
|
@@ -1,38 +1,94 @@
|
|
|
1
|
-
import type { IncomingMessage, Server
|
|
2
|
-
import { createServer } from "node:http"
|
|
1
|
+
import type { IncomingMessage, Server } from "node:http"
|
|
2
|
+
import { createServer, ServerResponse } from "node:http"
|
|
3
|
+
import type { Socket } from "node:net"
|
|
3
4
|
import { Readable, type Duplex } from "node:stream"
|
|
4
5
|
import { pipeline } from "node:stream/promises"
|
|
5
6
|
import type { Honey } from "./index.ts"
|
|
7
|
+
import { bodyKind, rawBodyOf } from "./body-kind.ts"
|
|
6
8
|
import { isHoneyResponse } from "./honey-response.ts"
|
|
7
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
ABORT_REQUEST,
|
|
11
|
+
BODY_OVERFLOW,
|
|
12
|
+
DEFAULT_MAX_REQUEST_BODY,
|
|
13
|
+
incomingToNodeRequest,
|
|
14
|
+
nodeRequestUrl,
|
|
15
|
+
type NodeRequestOptions,
|
|
16
|
+
} from "./node-request.ts"
|
|
17
|
+
import type { WSAdapter } from "./ws/cloudflare.ts"
|
|
18
|
+
import type { NodeUpgradeData } from "./ws/node.ts"
|
|
8
19
|
|
|
9
|
-
type
|
|
20
|
+
export type NodeServeOptions<TEnv> = {
|
|
10
21
|
env: TEnv
|
|
11
22
|
hostname?: string
|
|
12
23
|
port?: number
|
|
24
|
+
/**
|
|
25
|
+
* Largest request body a read accepts, in bytes. A bigger one fails the read with 413 and the
|
|
26
|
+
* connection is closed. `0` disables the cap. Default 128 MiB, as on Bun.
|
|
27
|
+
*/
|
|
28
|
+
maxRequestBodySize?: number
|
|
29
|
+
/** Node's `server.headersTimeout`, in ms (Node's default: 60 000). */
|
|
30
|
+
headersTimeout?: number
|
|
31
|
+
/** Node's `server.requestTimeout`, in ms (Node's default: 300 000). */
|
|
32
|
+
requestTimeout?: number
|
|
33
|
+
/** Node's `server.keepAliveTimeout`, in ms (Node's default: 5 000). */
|
|
34
|
+
keepAliveTimeout?: number
|
|
35
|
+
/**
|
|
36
|
+
* How long an upgrade request may take to become a WebSocket (or be answered), in ms.
|
|
37
|
+
* Default 30 000.
|
|
38
|
+
*/
|
|
39
|
+
upgradeTimeout?: number
|
|
40
|
+
/** Errors the adapter could not hand to the app (it then answers 500). Default: `console.error`. */
|
|
41
|
+
onError?: (error: unknown) => void
|
|
13
42
|
}
|
|
14
43
|
|
|
15
44
|
/** Buffer known-size bodies up to this many bytes, then `res.end(buf)`. */
|
|
16
45
|
const BUFFER_BODY_MAX = 256 * 1024
|
|
17
|
-
const
|
|
46
|
+
const DEFAULT_UPGRADE_TIMEOUT = 30_000
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Hop-by-hop headers (RFC 9110 §7.6.1): they describe one connection, so a response from
|
|
50
|
+
* somewhere else (a proxied `fetch()`) must not carry them onto this one. Node writes its own.
|
|
51
|
+
*/
|
|
52
|
+
const HOP_BY_HOP = new Set([
|
|
53
|
+
"connection",
|
|
54
|
+
"keep-alive",
|
|
55
|
+
"proxy-connection",
|
|
56
|
+
"te",
|
|
57
|
+
"trailer",
|
|
58
|
+
"transfer-encoding",
|
|
59
|
+
"upgrade",
|
|
60
|
+
])
|
|
18
61
|
|
|
19
62
|
// Node's `Readable.fromWeb` wants `node:stream/web` streams; DOM/Bun brands do not overlap.
|
|
20
63
|
function asNodeWebStream(stream: ReadableStream<Uint8Array>): import("node:stream/web").ReadableStream {
|
|
21
64
|
return stream as unknown as import("node:stream/web").ReadableStream
|
|
22
65
|
}
|
|
23
66
|
|
|
24
|
-
|
|
25
|
-
|
|
67
|
+
/** `null` when the request target or `Host` is invalid (see `nodeRequestUrl`); the caller answers 400. */
|
|
68
|
+
function incomingToRequest(req: IncomingMessage, opts: NodeRequestOptions): Request | null {
|
|
69
|
+
const url = nodeRequestUrl(req)
|
|
70
|
+
return url === null ? null : incomingToNodeRequest(req, url, opts)
|
|
26
71
|
}
|
|
27
72
|
|
|
28
73
|
function collectNodeHeaders(response: Response, extra?: Record<string, string>): Record<string, string | string[]> {
|
|
29
74
|
const headerObj: Record<string, string | string[]> = {}
|
|
30
75
|
let hasSetCookie = false
|
|
76
|
+
/* headers a `Connection` header names are hop-by-hop too */
|
|
77
|
+
const named = response.headers.get("connection")
|
|
78
|
+
const dropNamed =
|
|
79
|
+
named === null
|
|
80
|
+
? null
|
|
81
|
+
: named
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
.split(",")
|
|
84
|
+
.map((t) => t.trim())
|
|
31
85
|
response.headers.forEach((value, key) => {
|
|
32
86
|
if (key === "set-cookie") {
|
|
33
87
|
hasSetCookie = true
|
|
34
88
|
return
|
|
35
89
|
}
|
|
90
|
+
if (HOP_BY_HOP.has(key) && !(key === "upgrade" && response.status === 101)) return
|
|
91
|
+
if (dropNamed !== null && dropNamed.includes(key)) return
|
|
36
92
|
headerObj[key] = value
|
|
37
93
|
})
|
|
38
94
|
if (hasSetCookie) {
|
|
@@ -43,25 +99,40 @@ function collectNodeHeaders(response: Response, extra?: Record<string, string>):
|
|
|
43
99
|
return headerObj
|
|
44
100
|
}
|
|
45
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Buffer a body only when its size is known and small: a body Honey built in memory, or one
|
|
104
|
+
* that declares `content-length`. Everything else (SSE, `generate()`, a proxied stream, an
|
|
105
|
+
* untagged `new Response(stream)`) is piped as it is produced, whatever its content type.
|
|
106
|
+
*/
|
|
46
107
|
function shouldBufferBody(response: Response): boolean {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
108
|
+
if (bodyKind(response) !== "buffered") return false
|
|
109
|
+
const len = Number(response.headers.get("content-length"))
|
|
110
|
+
return Number.isFinite(len) && len <= BUFFER_BODY_MAX
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pipe a body to the socket. When the response closes first (client gone, shutdown), the
|
|
115
|
+
* reader is cancelled so the producer stops; nothing is left reading.
|
|
116
|
+
*/
|
|
117
|
+
async function pipeBody(stream: ReadableStream<Uint8Array>, res: ServerResponse): Promise<void> {
|
|
118
|
+
const readable = Readable.fromWeb(asNodeWebStream(stream))
|
|
119
|
+
const onClose = (): void => {
|
|
120
|
+
if (!res.writableFinished) readable.destroy()
|
|
121
|
+
}
|
|
122
|
+
res.once("close", onClose)
|
|
123
|
+
try {
|
|
124
|
+
await pipeline(readable, res)
|
|
125
|
+
} catch {
|
|
126
|
+
if (!res.destroyed) res.destroy()
|
|
127
|
+
/* `destroy()` cancels the web reader; this covers a stream that errored on its own */
|
|
128
|
+
stream.cancel().catch(() => {})
|
|
129
|
+
} finally {
|
|
130
|
+
res.off("close", onClose)
|
|
51
131
|
}
|
|
52
|
-
const ct = response.headers.get("content-type") ?? ""
|
|
53
|
-
if (ct.startsWith("text/event-stream")) return false
|
|
54
|
-
return (
|
|
55
|
-
ct.startsWith("application/json") ||
|
|
56
|
-
ct.startsWith("text/plain") ||
|
|
57
|
-
ct.startsWith("text/html") ||
|
|
58
|
-
ct.startsWith("text/csv") ||
|
|
59
|
-
ct.startsWith("application/xml")
|
|
60
|
-
)
|
|
61
132
|
}
|
|
62
133
|
|
|
63
|
-
function
|
|
64
|
-
return
|
|
134
|
+
function statusMessage(response: Response): string | undefined {
|
|
135
|
+
return response.statusText === "" ? undefined : response.statusText
|
|
65
136
|
}
|
|
66
137
|
|
|
67
138
|
async function responseToNode(response: Response, res: ServerResponse): Promise<void> {
|
|
@@ -80,25 +151,25 @@ async function responseToNode(response: Response, res: ServerResponse): Promise<
|
|
|
80
151
|
return
|
|
81
152
|
}
|
|
82
153
|
res.writeHead(response.status, response.plainHeaders)
|
|
83
|
-
|
|
84
|
-
await pipeline(Readable.fromWeb(asNodeWebStream(stream)), res)
|
|
85
|
-
} catch {
|
|
86
|
-
if (!res.destroyed) res.destroy()
|
|
87
|
-
}
|
|
154
|
+
await pipeBody(stream, res)
|
|
88
155
|
return
|
|
89
156
|
}
|
|
90
157
|
|
|
91
158
|
const raw = rawBodyOf(response)
|
|
92
159
|
if (typeof raw === "string" || raw instanceof Uint8Array) {
|
|
93
160
|
const byteLength = typeof raw === "string" ? Buffer.byteLength(raw) : raw.byteLength
|
|
94
|
-
res.writeHead(
|
|
161
|
+
res.writeHead(
|
|
162
|
+
response.status,
|
|
163
|
+
statusMessage(response),
|
|
164
|
+
collectNodeHeaders(response, { "content-length": String(byteLength) }),
|
|
165
|
+
)
|
|
95
166
|
res.end(raw)
|
|
96
167
|
return
|
|
97
168
|
}
|
|
98
169
|
|
|
99
170
|
const body = response.body
|
|
100
171
|
if (body === null) {
|
|
101
|
-
res.writeHead(response.status, collectNodeHeaders(response))
|
|
172
|
+
res.writeHead(response.status, statusMessage(response), collectNodeHeaders(response))
|
|
102
173
|
res.end()
|
|
103
174
|
return
|
|
104
175
|
}
|
|
@@ -106,17 +177,79 @@ async function responseToNode(response: Response, res: ServerResponse): Promise<
|
|
|
106
177
|
if (shouldBufferBody(response)) {
|
|
107
178
|
const buf = Buffer.from(await response.arrayBuffer())
|
|
108
179
|
if (res.destroyed) return
|
|
109
|
-
res.writeHead(
|
|
180
|
+
res.writeHead(
|
|
181
|
+
response.status,
|
|
182
|
+
statusMessage(response),
|
|
183
|
+
collectNodeHeaders(response, { "content-length": String(buf.byteLength) }),
|
|
184
|
+
)
|
|
110
185
|
res.end(buf)
|
|
111
186
|
return
|
|
112
187
|
}
|
|
113
188
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
189
|
+
const headers = collectNodeHeaders(response)
|
|
190
|
+
/* a stream's declared length is never trusted (`fetch()` keeps the encoded length of a body it
|
|
191
|
+
* decoded): Node chunks it, so a wrong length cannot truncate it or desynchronize keep-alive */
|
|
192
|
+
delete headers["content-length"]
|
|
193
|
+
res.writeHead(response.status, statusMessage(response), headers)
|
|
194
|
+
await pipeBody(body, res)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function isWebSocketUpgrade(req: IncomingMessage): boolean {
|
|
198
|
+
return req.headers.upgrade?.toLowerCase() === "websocket"
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The body of a request that came in on Node's `'upgrade'` path but is served as a normal
|
|
203
|
+
* request. Node with `shouldUpgradeCallback` never sends those there; before that,
|
|
204
|
+
* some versions leave the body on the socket (the first bytes in `head`). Only a declared
|
|
205
|
+
* `content-length` can be read back that way.
|
|
206
|
+
*/
|
|
207
|
+
function upgradeBodySource(
|
|
208
|
+
req: IncomingMessage,
|
|
209
|
+
socket: Duplex,
|
|
210
|
+
head: Buffer,
|
|
211
|
+
): AsyncIterable<Buffer> | null | "unsupported" {
|
|
212
|
+
/* newer Node parses the body into `req` itself */
|
|
213
|
+
if (!req.complete) return null
|
|
214
|
+
if (req.headers["transfer-encoding"] !== undefined) return "unsupported"
|
|
215
|
+
const declared = Number(req.headers["content-length"] ?? 0)
|
|
216
|
+
if (!Number.isSafeInteger(declared) || declared <= 0) return null
|
|
217
|
+
return (async function* () {
|
|
218
|
+
let remaining = declared
|
|
219
|
+
if (head.byteLength > 0) {
|
|
220
|
+
const first = head.subarray(0, remaining)
|
|
221
|
+
remaining -= first.byteLength
|
|
222
|
+
yield first
|
|
223
|
+
}
|
|
224
|
+
if (remaining <= 0) return
|
|
225
|
+
for await (const chunk of socket as AsyncIterable<Buffer>) {
|
|
226
|
+
const part = chunk.subarray(0, remaining)
|
|
227
|
+
remaining -= part.byteLength
|
|
228
|
+
yield part
|
|
229
|
+
if (remaining <= 0) return
|
|
230
|
+
}
|
|
231
|
+
})()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Answer on a socket Node handed over through `'upgrade'`, with a real `ServerResponse`. */
|
|
235
|
+
function responseOnSocket(req: IncomingMessage, socket: Socket): ServerResponse {
|
|
236
|
+
const res = new ServerResponse(req)
|
|
237
|
+
/* the connection ends with this response */
|
|
238
|
+
res.shouldKeepAlive = false
|
|
239
|
+
res.assignSocket(socket)
|
|
240
|
+
res.once("finish", () => {
|
|
241
|
+
res.detachSocket(socket)
|
|
242
|
+
socket.end()
|
|
243
|
+
})
|
|
244
|
+
return res
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** A response written before any Request exists (bad target or `Host`): plain text, then close. */
|
|
248
|
+
function writeRawStatus(socket: Duplex, status: 400 | 503, text: string): void {
|
|
249
|
+
if (socket.destroyed) return
|
|
250
|
+
socket.end(
|
|
251
|
+
`HTTP/1.1 ${status} ${text}\r\nconnection: close\r\ncontent-type: text/plain\r\ncontent-length: ${text.length}\r\n\r\n${text}`,
|
|
252
|
+
)
|
|
120
253
|
}
|
|
121
254
|
|
|
122
255
|
export type HoneyServer = Server & {
|
|
@@ -125,83 +258,193 @@ export type HoneyServer = Server & {
|
|
|
125
258
|
|
|
126
259
|
export function serve<TEnv>(
|
|
127
260
|
app: Honey<TEnv, unknown, unknown, unknown, unknown, string, string>,
|
|
128
|
-
options:
|
|
261
|
+
options: NodeServeOptions<TEnv>,
|
|
129
262
|
): HoneyServer {
|
|
130
263
|
const env = options.env
|
|
131
|
-
|
|
264
|
+
const requestOpts: NodeRequestOptions = { maxBodySize: options.maxRequestBodySize ?? DEFAULT_MAX_REQUEST_BODY }
|
|
265
|
+
const upgradeTimeout = options.upgradeTimeout ?? DEFAULT_UPGRADE_TIMEOUT
|
|
266
|
+
const reportError =
|
|
267
|
+
options.onError ??
|
|
268
|
+
((err: unknown): void => {
|
|
269
|
+
console.error("honey: request failed in the Node adapter", err)
|
|
270
|
+
})
|
|
271
|
+
/* in-flight requests and their responses: shutdown aborts their signals */
|
|
272
|
+
const inflight = new Map<ServerResponse, Request>()
|
|
273
|
+
/* sockets that became WebSockets: shutdown closes them */
|
|
274
|
+
const upgraded = new Set<Duplex>()
|
|
132
275
|
let draining = false
|
|
133
|
-
let drainResolve: (() => void) | null = null
|
|
134
276
|
|
|
135
|
-
const
|
|
277
|
+
const abort = (request: Request, reason: unknown): void => {
|
|
278
|
+
;(request as unknown as { [ABORT_REQUEST]?: (r?: unknown) => void })[ABORT_REQUEST]?.(reason)
|
|
279
|
+
}
|
|
280
|
+
const wsAdapter = (): WSAdapter | null =>
|
|
281
|
+
(app as unknown as { _graph: { settings: { wsAdapter: WSAdapter | null } } })._graph.settings.wsAdapter
|
|
282
|
+
|
|
283
|
+
/** One HTTP request, whether it came in normally or on the `'upgrade'` path. */
|
|
284
|
+
const handle = async (
|
|
285
|
+
req: IncomingMessage,
|
|
286
|
+
res: ServerResponse,
|
|
287
|
+
bodySource: AsyncIterable<Buffer> | null = null,
|
|
288
|
+
): Promise<void> => {
|
|
136
289
|
if (draining) {
|
|
137
|
-
res.writeHead(503)
|
|
290
|
+
res.writeHead(503, { connection: "close" })
|
|
138
291
|
res.end("Service Unavailable")
|
|
139
292
|
return
|
|
140
293
|
}
|
|
141
|
-
|
|
294
|
+
let request: Request | null = null
|
|
142
295
|
try {
|
|
143
|
-
|
|
296
|
+
request = incomingToRequest(req, bodySource === null ? requestOpts : { ...requestOpts, bodySource })
|
|
297
|
+
if (request === null) {
|
|
298
|
+
res.writeHead(400, { connection: "close", "content-type": "text/plain" })
|
|
299
|
+
res.end("Bad Request")
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
const tracked = request
|
|
303
|
+
inflight.set(res, tracked)
|
|
304
|
+
/* the response closed before it finished: the client left (or shutdown cut it) */
|
|
305
|
+
res.once("close", () => {
|
|
306
|
+
if (!res.writableFinished) abort(tracked, new DOMException("The client disconnected", "AbortError"))
|
|
307
|
+
})
|
|
144
308
|
const maybe = app.fetch(request, env)
|
|
145
309
|
const response = maybe instanceof Promise ? await maybe : maybe
|
|
310
|
+
/* while draining, tell keep-alive clients this connection ends with the response */
|
|
311
|
+
if (draining) res.shouldKeepAlive = false
|
|
312
|
+
/* a body over the cap was left unread on the socket: never reuse the connection */
|
|
313
|
+
if ((request as unknown as { [BODY_OVERFLOW]?: boolean })[BODY_OVERFLOW] === true) res.shouldKeepAlive = false
|
|
146
314
|
await responseToNode(response, res)
|
|
147
|
-
} catch {
|
|
315
|
+
} catch (err) {
|
|
316
|
+
reportError(err)
|
|
148
317
|
if (!res.headersSent) {
|
|
149
|
-
res.writeHead(500)
|
|
318
|
+
res.writeHead(500, { "content-type": "text/plain" })
|
|
150
319
|
res.end("Internal Server Error")
|
|
151
320
|
} else if (!res.destroyed) {
|
|
152
321
|
res.destroy()
|
|
153
322
|
}
|
|
154
323
|
} finally {
|
|
155
|
-
inflight
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
324
|
+
if (request !== null) inflight.delete(res)
|
|
325
|
+
/* a keep-alive connection that just went idle would hold `close()` open until it times out */
|
|
326
|
+
if (draining) setImmediate(() => server.closeIdleConnections())
|
|
159
327
|
}
|
|
160
|
-
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const server = createServer(
|
|
331
|
+
{
|
|
332
|
+
/* only a WebSocket handshake takes the upgrade path; `Upgrade: h2c` and the like are
|
|
333
|
+
* served as the normal requests they are (where Node has `shouldUpgradeCallback`; older Node ignores the option) */
|
|
334
|
+
shouldUpgradeCallback: isWebSocketUpgrade,
|
|
335
|
+
} as Parameters<typeof createServer>[0],
|
|
336
|
+
(req, res) => {
|
|
337
|
+
void handle(req, res)
|
|
338
|
+
},
|
|
339
|
+
)
|
|
340
|
+
if (options.headersTimeout !== undefined) server.headersTimeout = options.headersTimeout
|
|
341
|
+
if (options.requestTimeout !== undefined) server.requestTimeout = options.requestTimeout
|
|
342
|
+
if (options.keepAliveTimeout !== undefined) server.keepAliveTimeout = options.keepAliveTimeout
|
|
161
343
|
|
|
162
|
-
|
|
163
|
-
|
|
344
|
+
/** A WebSocket handshake, or (on Node without `shouldUpgradeCallback`) any request with `Upgrade`. */
|
|
345
|
+
const handleUpgrade = async (req: IncomingMessage, socket: Socket, head: Buffer): Promise<void> => {
|
|
346
|
+
if (!isWebSocketUpgrade(req)) {
|
|
347
|
+
const source = upgradeBodySource(req, socket, head)
|
|
348
|
+
if (source === "unsupported") {
|
|
349
|
+
writeRawStatus(socket, 400, "Bad Request")
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
socket.setTimeout(0)
|
|
353
|
+
await handle(req, responseOnSocket(req, socket), source)
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
if (draining) {
|
|
357
|
+
writeRawStatus(socket, 503, "Service Unavailable")
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
const request = incomingToRequest(req, requestOpts)
|
|
361
|
+
if (request === null) {
|
|
362
|
+
writeRawStatus(socket, 400, "Bad Request")
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
socket.once("close", () => {
|
|
366
|
+
abort(request, new DOMException("The client disconnected", "AbortError"))
|
|
367
|
+
})
|
|
368
|
+
const state: NodeUpgradeData = { head, req, socket, upgraded: false }
|
|
369
|
+
const envWithUpgrade = { ...env, __nodeUpgrade: state } as TEnv
|
|
370
|
+
let response: Response
|
|
164
371
|
try {
|
|
165
|
-
const request = incomingToRequest(req)
|
|
166
|
-
const envWithUpgrade = { ...env, __nodeUpgrade: { head, req, socket } } as TEnv
|
|
167
372
|
const maybe = app.fetch(request, envWithUpgrade)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
)
|
|
177
|
-
socket.destroy()
|
|
373
|
+
response = maybe instanceof Promise ? await maybe : maybe
|
|
374
|
+
} catch (err) {
|
|
375
|
+
reportError(err)
|
|
376
|
+
/* once ws wrote the 101 the socket is a WebSocket: an HTTP status would corrupt it */
|
|
377
|
+
if (!state.upgraded) {
|
|
378
|
+
socket.setTimeout(0)
|
|
379
|
+
const res = responseOnSocket(req, socket)
|
|
380
|
+
res.writeHead(500, { "content-type": "text/plain" })
|
|
381
|
+
res.end("Internal Server Error")
|
|
178
382
|
}
|
|
179
|
-
|
|
180
|
-
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
if (state.upgraded) {
|
|
386
|
+
socket.setTimeout(0)
|
|
387
|
+
upgraded.add(socket)
|
|
388
|
+
socket.once("close", () => upgraded.delete(socket))
|
|
389
|
+
return
|
|
390
|
+
}
|
|
391
|
+
if (socket.destroyed) {
|
|
392
|
+
await response.body?.cancel().catch(() => {})
|
|
393
|
+
return
|
|
394
|
+
}
|
|
395
|
+
/* not upgraded (auth rejected, 404, 426, an SSE route…): a real response, streamed, every header kept */
|
|
396
|
+
socket.setTimeout(0)
|
|
397
|
+
try {
|
|
398
|
+
await responseToNode(response, responseOnSocket(req, socket))
|
|
399
|
+
} catch (err) {
|
|
400
|
+
reportError(err)
|
|
181
401
|
socket.destroy()
|
|
182
402
|
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
server.on("upgrade", (req: IncomingMessage, duplex: Duplex, head: Buffer) => {
|
|
406
|
+
const socket = duplex as Socket
|
|
407
|
+
/* first: a reset while the app is still deciding must not become an uncaught 'error' event */
|
|
408
|
+
socket.on("error", () => {
|
|
409
|
+
socket.destroy()
|
|
410
|
+
})
|
|
411
|
+
if (upgradeTimeout > 0) socket.setTimeout(upgradeTimeout, () => socket.destroy())
|
|
412
|
+
handleUpgrade(req, socket, head).catch((err: unknown) => {
|
|
413
|
+
reportError(err)
|
|
414
|
+
socket.destroy()
|
|
415
|
+
})
|
|
183
416
|
})
|
|
184
417
|
|
|
185
418
|
server.listen(options?.port ?? 0, options?.hostname)
|
|
186
419
|
|
|
187
420
|
const honeyServer = server as HoneyServer
|
|
421
|
+
/**
|
|
422
|
+
* Stop accepting connections and let in-flight requests finish. Responses already streaming
|
|
423
|
+
* (SSE, `generate()`) never finish on their own, so their `ctx.signal` aborts now and they
|
|
424
|
+
* end; WebSockets get a 1001 close. Handlers still working get until `timeout`; then their
|
|
425
|
+
* signal aborts and every connection, WebSockets included, is closed.
|
|
426
|
+
*/
|
|
188
427
|
honeyServer.shutdown = (timeout?: number): Promise<void> => {
|
|
189
428
|
draining = true
|
|
429
|
+
const shutdownReason = (): DOMException => new DOMException("The server is shutting down", "AbortError")
|
|
190
430
|
|
|
191
431
|
return new Promise<void>((resolve) => {
|
|
432
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
433
|
+
/* fires once every connection has closed */
|
|
192
434
|
server.close(() => {
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
return
|
|
196
|
-
}
|
|
197
|
-
drainResolve = resolve
|
|
435
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
436
|
+
resolve()
|
|
198
437
|
})
|
|
199
|
-
|
|
438
|
+
server.closeIdleConnections()
|
|
439
|
+
for (const [res, request] of inflight) {
|
|
440
|
+
if (res.headersSent) abort(request, shutdownReason())
|
|
441
|
+
}
|
|
442
|
+
wsAdapter()?.closeAll?.(1001, "server shutting down")
|
|
200
443
|
if (timeout !== undefined) {
|
|
201
|
-
setTimeout(() => {
|
|
202
|
-
|
|
444
|
+
timer = setTimeout(() => {
|
|
445
|
+
for (const request of inflight.values()) abort(request, shutdownReason())
|
|
446
|
+
for (const socket of upgraded) socket.destroy()
|
|
203
447
|
server.closeAllConnections()
|
|
204
|
-
resolve()
|
|
205
448
|
}, timeout)
|
|
206
449
|
}
|
|
207
450
|
})
|
package/src/openapi/collect.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { parsePattern, toOpenApiTemplate, UNNAMED_WILDCARD } from "../pattern.ts"
|
|
2
|
+
import type { RouteHandler, WSRouteHandler } from "../tree.ts"
|
|
2
3
|
import type { InputSchemaEntry, StandardSchemaLike } from "../types.ts"
|
|
3
4
|
|
|
4
5
|
export type CollectedRoute = {
|
|
@@ -12,59 +13,33 @@ export type CollectedWSRoute = {
|
|
|
12
13
|
path: string
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
/** Path parameter names of a route pattern, as OpenAPI names them (wildcards included). */
|
|
15
17
|
export function extractParams(path: string): string[] {
|
|
16
|
-
const
|
|
17
|
-
for (const seg of path
|
|
18
|
-
if (seg.
|
|
19
|
-
|
|
20
|
-
}
|
|
18
|
+
const out: string[] = []
|
|
19
|
+
for (const seg of parsePattern(path)) {
|
|
20
|
+
if (seg.k === "param") out.push(seg.n)
|
|
21
|
+
else if (seg.k === "wildcard") out.push(seg.n === UNNAMED_WILDCARD ? "wildcard" : seg.n)
|
|
21
22
|
}
|
|
22
|
-
return
|
|
23
|
+
return out
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function toOpenApiPath(path: string): string {
|
|
26
|
-
return
|
|
27
|
+
return toOpenApiTemplate(parsePattern(path))
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
includeSkipped?: boolean,
|
|
34
|
-
): void {
|
|
35
|
-
if (node.m !== null) {
|
|
36
|
-
for (const [method, handler] of Object.entries(node.m)) {
|
|
37
|
-
if (handler._skip && !includeSkipped) continue
|
|
38
|
-
routes.push({ handler, method, path: currentPath || "/" })
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
for (const [seg, child] of Object.entries(node.s)) {
|
|
43
|
-
walkTree(child, `${currentPath}/${seg}`, routes, includeSkipped)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (node.d !== null) {
|
|
47
|
-
walkTree(node.d.c, `${currentPath}/:${node.d.n}`, routes, includeSkipped)
|
|
48
|
-
}
|
|
30
|
+
type RouteSource = {
|
|
31
|
+
_collectRoutes(includeSkipped?: boolean): CollectedRoute[]
|
|
32
|
+
_collectWsRoutes(): CollectedWSRoute[]
|
|
33
|
+
}
|
|
49
34
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
routes.push({ handler, method, path: `${currentPath}/*${node.w.n}` })
|
|
54
|
-
}
|
|
55
|
-
}
|
|
35
|
+
/** Every served HTTP route of an app, one entry per concrete path. */
|
|
36
|
+
export function collectRoutes(app: unknown, includeSkipped?: boolean): CollectedRoute[] {
|
|
37
|
+
return (app as RouteSource)._collectRoutes(includeSkipped)
|
|
56
38
|
}
|
|
57
39
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
for (const [seg, child] of Object.entries(node.s)) {
|
|
63
|
-
walkWSRoutes(child, `${currentPath}/${seg}`, routes)
|
|
64
|
-
}
|
|
65
|
-
if (node.d !== null) {
|
|
66
|
-
walkWSRoutes(node.d.c, `${currentPath}/:${node.d.n}`, routes)
|
|
67
|
-
}
|
|
40
|
+
/** Every websocket route of an app, one entry per concrete path. */
|
|
41
|
+
export function collectWsRoutes(app: unknown): CollectedWSRoute[] {
|
|
42
|
+
return (app as RouteSource)._collectWsRoutes()
|
|
68
43
|
}
|
|
69
44
|
|
|
70
45
|
export function unwrapEntry(entry: InputSchemaEntry): StandardSchemaLike {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { TypedResponse } from "../response.ts"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pinned docs UI assets with Subresource Integrity. The page runs on the API's own origin, so
|
|
5
|
+
* an unpinned CDN script could read anything the origin can; a pinned version with an
|
|
6
|
+
* integrity hash can only ever be these bytes.
|
|
7
|
+
*/
|
|
8
|
+
export const SWAGGER_UI = {
|
|
9
|
+
css: {
|
|
10
|
+
integrity: "sha384-Ov4/wv3j2bmct8cDc5X4ngJZohVPzEmc6uDPH8WeljUxO5vtoykvMEfbu9Vh6RaW",
|
|
11
|
+
url: "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.1/swagger-ui.css",
|
|
12
|
+
},
|
|
13
|
+
js: {
|
|
14
|
+
integrity: "sha384-ZPehFMQommnnuaZ4rpxgkgTT2DKFVp4hZC/7pLit+9Lek9T1YGSo23eHFbvNkXkw",
|
|
15
|
+
url: "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.1/swagger-ui-bundle.js",
|
|
16
|
+
},
|
|
17
|
+
} as const
|
|
18
|
+
|
|
19
|
+
export const SCALAR = {
|
|
20
|
+
js: {
|
|
21
|
+
integrity: "sha384-kYDGzV91Jnn3TbHINV3nt54riK2uMJDfN5Al8dAkz4FssELTBWbD8rgw32sTKfOi",
|
|
22
|
+
url: "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.73.1/dist/browser/standalone.js",
|
|
23
|
+
},
|
|
24
|
+
} as const
|
|
25
|
+
|
|
26
|
+
const CDN_ORIGIN = "https://cdn.jsdelivr.net"
|
|
27
|
+
|
|
28
|
+
/* built from char codes: a formatter may turn an escape in a literal into the raw separator */
|
|
29
|
+
const LINE_SEPARATOR = String.fromCharCode(0x2028)
|
|
30
|
+
const PARAGRAPH_SEPARATOR = String.fromCharCode(0x2029)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* JSON for an inline `<script>`: `<` cannot end the element, and U+2028/U+2029 cannot end a
|
|
34
|
+
* string literal in older engines.
|
|
35
|
+
*/
|
|
36
|
+
export function inlineJson(value: unknown): string {
|
|
37
|
+
return JSON.stringify(value)
|
|
38
|
+
.replace(/</g, "\\u003c")
|
|
39
|
+
.replace(/>/g, "\\u003e")
|
|
40
|
+
.replace(/&/g, "\\u0026")
|
|
41
|
+
.replaceAll(LINE_SEPARATOR, "\\u2028")
|
|
42
|
+
.replaceAll(PARAGRAPH_SEPARATOR, "\\u2029")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function escapeAttribute(value: string): string {
|
|
46
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function nonce(): string {
|
|
50
|
+
const bytes = new Uint8Array(16)
|
|
51
|
+
crypto.getRandomValues(bytes)
|
|
52
|
+
let s = ""
|
|
53
|
+
for (const b of bytes) s += String.fromCharCode(b)
|
|
54
|
+
return btoa(s)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type DocsCtx = { res: { html(sk: "ok", body: string, opts?: { headers?: Record<string, string> }): TypedResponse } }
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A docs page handler: a fresh script nonce per response, a CSP that allows only that inline
|
|
61
|
+
* script and the pinned CDN, and `nosniff`.
|
|
62
|
+
*/
|
|
63
|
+
export function docsPage(render: (nonce: string) => string): (ctx: DocsCtx) => TypedResponse {
|
|
64
|
+
const handler = (ctx: DocsCtx) => {
|
|
65
|
+
const n = nonce()
|
|
66
|
+
const csp = [
|
|
67
|
+
"default-src 'none'",
|
|
68
|
+
`script-src 'nonce-${n}' ${CDN_ORIGIN}`,
|
|
69
|
+
`style-src 'self' 'unsafe-inline' ${CDN_ORIGIN}`,
|
|
70
|
+
"img-src 'self' data: https:",
|
|
71
|
+
"font-src 'self' data: https:",
|
|
72
|
+
"connect-src 'self' https:",
|
|
73
|
+
"worker-src blob:",
|
|
74
|
+
"base-uri 'none'",
|
|
75
|
+
"form-action 'none'",
|
|
76
|
+
"frame-ancestors 'self'",
|
|
77
|
+
"object-src 'none'",
|
|
78
|
+
].join("; ")
|
|
79
|
+
return ctx.res.html("ok", render(n), {
|
|
80
|
+
headers: {
|
|
81
|
+
"content-security-policy": csp,
|
|
82
|
+
"referrer-policy": "no-referrer",
|
|
83
|
+
"x-content-type-options": "nosniff",
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
Object.defineProperty(handler, Symbol.for("honey.internal"), { value: true })
|
|
88
|
+
return handler
|
|
89
|
+
}
|