@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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const PLAIN = 0
|
|
2
|
+
/** a character the slow path rewrites: a backslash, or one `encodeSegment` percent-encodes */
|
|
3
|
+
const SLOW = 1
|
|
4
|
+
const PERCENT = 2
|
|
5
|
+
const SLASH = 3
|
|
6
|
+
|
|
7
|
+
const CHAR_CLASS = new Uint8Array(128)
|
|
8
|
+
for (let c = 0; c < 128; c++) {
|
|
9
|
+
if (c <= 0x20 || c === 0x7f || c === 92 || needsEncoding(c)) CHAR_CLASS[c] = SLOW
|
|
10
|
+
}
|
|
11
|
+
CHAR_CLASS[37] = PERCENT
|
|
12
|
+
CHAR_CLASS[47] = SLASH
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The request path the router, scopes, `ctx.path`, `proxy()` and `staticFiles()` all see.
|
|
16
|
+
*
|
|
17
|
+
* Policy, applied the same way on every runtime:
|
|
18
|
+
* - empty segments collapse: `//admin///users` is `/admin/users`
|
|
19
|
+
* - dot segments resolve: `.` is dropped and `..` removes the segment before it, never past
|
|
20
|
+
* the root; `%2e` counts as a dot, as it does for WHATWG URL parsing
|
|
21
|
+
* - a trailing slash is kept (the router and `trailingSlash()` decide what it means)
|
|
22
|
+
* - a backslash is a `/`, and characters a URL path cannot hold raw (space, controls, `"`, `<`,
|
|
23
|
+
* `>`, `` ` ``, `{`, `}`, non-ASCII) are percent-encoded as UTF-8 — what WHATWG URL parsing
|
|
24
|
+
* does, so a runtime that hands the app the raw target (Deno) and one that parses it first
|
|
25
|
+
* (Bun, Node) route the same bytes the same way
|
|
26
|
+
* - an encoded `/` or `\` (`%2F`, `%5C`) is rejected unless `encodedSlashes` is `"allow"`: a
|
|
27
|
+
* segment that decodes to a path separator means one thing to this router and another to
|
|
28
|
+
* whatever the path is forwarded to (a proxy upstream, a file system). Allowed, it stays
|
|
29
|
+
* encoded in the path and decodes into a param value.
|
|
30
|
+
* - everything else stays percent-encoded; params are decoded when they are extracted
|
|
31
|
+
*
|
|
32
|
+
* Returns `null` for a rejected path; the app answers 400.
|
|
33
|
+
*/
|
|
34
|
+
export function normalizePath(raw: string, encodedSlashes: "allow" | "reject" = "reject"): string | null {
|
|
35
|
+
const rejectEncoded = encodedSlashes === "reject"
|
|
36
|
+
let canonical = raw.charCodeAt(0) === 47
|
|
37
|
+
const len = raw.length
|
|
38
|
+
for (let i = 0; i < len; i++) {
|
|
39
|
+
const c = raw.charCodeAt(i)
|
|
40
|
+
const k = c < 128 ? CHAR_CLASS[c] : SLOW
|
|
41
|
+
if (k === PLAIN) continue
|
|
42
|
+
if (k === SLOW) {
|
|
43
|
+
canonical = false
|
|
44
|
+
} else if (k === PERCENT) {
|
|
45
|
+
const hi = raw.charCodeAt(i + 1)
|
|
46
|
+
const lo = raw.charCodeAt(i + 2) | 0x20
|
|
47
|
+
if (hi === 50 /* 2 */) {
|
|
48
|
+
if (lo === 102 /* f */ && rejectEncoded) return null
|
|
49
|
+
if (lo === 101 /* e */) canonical = false
|
|
50
|
+
} else if (hi === 53 /* 5 */ && lo === 99 /* c */ && rejectEncoded) {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
const n = raw.charCodeAt(i + 1)
|
|
55
|
+
if (n === 47 || n === 46 /* . */) canonical = false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (canonical) return raw
|
|
59
|
+
|
|
60
|
+
const out: string[] = []
|
|
61
|
+
const segments = raw.replaceAll("\\", "/").split("/")
|
|
62
|
+
let trailing = false
|
|
63
|
+
for (let i = 0; i < segments.length; i++) {
|
|
64
|
+
const seg = segments[i]
|
|
65
|
+
const last = i === segments.length - 1
|
|
66
|
+
if (seg === "") {
|
|
67
|
+
if (last && i > 0) trailing = true
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
const dots = dotSegment(seg)
|
|
71
|
+
if (dots === 0) {
|
|
72
|
+
out.push(encodeSegment(seg))
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
if (dots === 2) out.pop()
|
|
76
|
+
/* `/a/b/..` is `/a/`, like URL resolution */
|
|
77
|
+
if (last) trailing = true
|
|
78
|
+
}
|
|
79
|
+
if (out.length === 0) return "/"
|
|
80
|
+
const path = `/${out.join("/")}`
|
|
81
|
+
return trailing ? `${path}/` : path
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The WHATWG path percent-encode set, plus every non-ASCII code unit. `^` joined the set in
|
|
86
|
+
* 2023; Node and Bun encode it, Deno's parser does not yet, so it is encoded here to keep
|
|
87
|
+
* `ctx.path` the same on all three.
|
|
88
|
+
*/
|
|
89
|
+
function needsEncoding(c: number): boolean {
|
|
90
|
+
return (
|
|
91
|
+
c <= 0x20 ||
|
|
92
|
+
c >= 0x7f ||
|
|
93
|
+
c === 34 /* " */ ||
|
|
94
|
+
c === 60 /* < */ ||
|
|
95
|
+
c === 62 /* > */ ||
|
|
96
|
+
c === 94 /* ^ */ ||
|
|
97
|
+
c === 96 /* ` */ ||
|
|
98
|
+
c === 123 /* { */ ||
|
|
99
|
+
c === 125 /* } */
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* One path segment as a normalized request path holds it: characters a URL path cannot hold
|
|
105
|
+
* raw become UTF-8 percent escapes, everything else (existing escapes included) is kept.
|
|
106
|
+
* Route patterns encode their static segments with this too, so `app.get("/é")` matches the
|
|
107
|
+
* `/%C3%A9` every runtime hands the app.
|
|
108
|
+
*/
|
|
109
|
+
export function encodeSegment(seg: string): string {
|
|
110
|
+
let needs = false
|
|
111
|
+
for (let i = 0; i < seg.length; i++) {
|
|
112
|
+
if (needsEncoding(seg.charCodeAt(i))) {
|
|
113
|
+
needs = true
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!needs) return seg
|
|
118
|
+
let out = ""
|
|
119
|
+
for (const ch of seg) {
|
|
120
|
+
const c = ch.charCodeAt(0)
|
|
121
|
+
if (ch.length === 1 && !needsEncoding(c)) {
|
|
122
|
+
out += ch
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
out += encodeURIComponent(ch)
|
|
127
|
+
} catch {
|
|
128
|
+
/* a lone surrogate: U+FFFD, as URL parsing encodes it */
|
|
129
|
+
out += "%EF%BF%BD"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return out
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 1 for `.`, 2 for `..` (either may be written `%2e`), 0 for anything else. */
|
|
136
|
+
function dotSegment(seg: string): 0 | 1 | 2 {
|
|
137
|
+
if (seg.length > 6) return 0
|
|
138
|
+
const s = seg.toLowerCase().replaceAll("%2e", ".")
|
|
139
|
+
if (s === ".") return 1
|
|
140
|
+
if (s === "..") return 2
|
|
141
|
+
return 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The path of a request URL without `new URL()`: everything from the first `/` after the authority, up to `?` or `#`. */
|
|
145
|
+
export function pathOfUrl(rawUrl: string): string {
|
|
146
|
+
const protoEnd = rawUrl.indexOf("//")
|
|
147
|
+
const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
|
|
148
|
+
if (pathStart === -1) return "/"
|
|
149
|
+
for (let i = pathStart; i < rawUrl.length; i++) {
|
|
150
|
+
const c = rawUrl.charCodeAt(i)
|
|
151
|
+
if (c === 63 /* ? */ || c === 35 /* # */) return rawUrl.substring(pathStart, i)
|
|
152
|
+
}
|
|
153
|
+
return rawUrl.substring(pathStart)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** `?query` of a request URL (with the `?`), or `""`. Never includes a fragment. */
|
|
157
|
+
export function searchOfUrl(rawUrl: string): string {
|
|
158
|
+
const protoEnd = rawUrl.indexOf("//")
|
|
159
|
+
const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
|
|
160
|
+
if (pathStart === -1) return ""
|
|
161
|
+
const q = rawUrl.indexOf("?", pathStart)
|
|
162
|
+
if (q === -1) return ""
|
|
163
|
+
const hash = rawUrl.indexOf("#", q)
|
|
164
|
+
return hash === -1 ? rawUrl.substring(q) : rawUrl.substring(q, hash)
|
|
165
|
+
}
|
package/src/request-to-curl.ts
CHANGED
|
@@ -2,30 +2,51 @@ export type RequestToCurlOptions = {
|
|
|
2
2
|
excludeHeader?: (name: string, value: string) => boolean
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
|
|
5
|
+
function hasControl(value: string): boolean {
|
|
6
|
+
for (let i = 0; i < value.length; i++) {
|
|
7
|
+
const code = value.charCodeAt(i)
|
|
8
|
+
if (code < 0x20 || code === 0x7f) return true
|
|
9
|
+
}
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Quote one shell word. Plain values use single quotes; values with control
|
|
15
|
+
* characters (CR, LF, ESC, …) use ANSI-C `$'…'` quoting with `\xHH` escapes, so
|
|
16
|
+
* a pasted command cannot carry a raw newline and a log line cannot be split.
|
|
17
|
+
*/
|
|
18
|
+
export function shellQuote(value: string): string {
|
|
19
|
+
if (!hasControl(value)) return `'${value.replace(/'/g, "'\\''")}'`
|
|
20
|
+
let out = "$'"
|
|
21
|
+
for (const ch of value) {
|
|
22
|
+
const code = ch.codePointAt(0) as number
|
|
23
|
+
if (ch === "\\" || ch === "'") out += `\\${ch}`
|
|
24
|
+
else if (code < 0x20 || code === 0x7f) out += `\\x${code.toString(16).padStart(2, "0")}`
|
|
25
|
+
else out += ch
|
|
26
|
+
}
|
|
27
|
+
return `${out}'`
|
|
7
28
|
}
|
|
8
29
|
|
|
9
30
|
/**
|
|
10
|
-
* Convert a native Request into a curl command string.
|
|
31
|
+
* Convert a native Request into a curl command string. Every word is quoted.
|
|
11
32
|
* The request is cloned so reading the body does not consume the original stream.
|
|
12
33
|
*/
|
|
13
34
|
export async function requestToCurl(request: Request, options?: RequestToCurlOptions): Promise<string> {
|
|
14
35
|
const clonedRequest = request.clone()
|
|
15
|
-
const parts: string[] = [
|
|
36
|
+
const parts: string[] = ["curl", "-X", shellQuote(clonedRequest.method)]
|
|
16
37
|
|
|
17
38
|
for (const [name, value] of clonedRequest.headers.entries()) {
|
|
18
39
|
if (options?.excludeHeader?.(name, value) === true) continue
|
|
19
|
-
parts.push(
|
|
40
|
+
parts.push("-H", shellQuote(`${name}: ${value}`))
|
|
20
41
|
}
|
|
21
42
|
|
|
22
43
|
const body = await clonedRequest.text()
|
|
23
44
|
|
|
24
45
|
if (body.length > 0) {
|
|
25
|
-
parts.push(
|
|
46
|
+
parts.push("--data-raw", shellQuote(body))
|
|
26
47
|
}
|
|
27
48
|
|
|
28
|
-
parts.push(
|
|
49
|
+
parts.push(shellQuote(clonedRequest.url))
|
|
29
50
|
|
|
30
51
|
return parts.join(" ")
|
|
31
52
|
}
|
package/src/response.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CookieOptions } from "./cookie.ts"
|
|
|
2
2
|
import { serializeCookie } from "./cookie.ts"
|
|
3
3
|
import type { HoneyError } from "./error.ts"
|
|
4
4
|
import { createHoneyResponse, isHoneyResponse } from "./honey-response.ts"
|
|
5
|
+
import { generatorStream, producerStream, tagStream, type ProducerOptions } from "./producer-stream.ts"
|
|
5
6
|
import type { StatusKey } from "./types.ts"
|
|
6
7
|
import { statusKeyToCode } from "./types.ts"
|
|
7
8
|
|
|
@@ -28,9 +29,19 @@ export type SSEOptions = ResponseOptions & {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export type SSEStream = {
|
|
32
|
+
/** End the stream. Idempotent and synchronous: safe in a `finally`. */
|
|
31
33
|
close(): void
|
|
34
|
+
/** True once the stream ended: closed, or the client went away. */
|
|
35
|
+
readonly closed: boolean
|
|
32
36
|
lastEventId: string | undefined
|
|
37
|
+
/**
|
|
38
|
+
* Queue an event. Resolves when the client is ready for more. After the stream ended it
|
|
39
|
+
* rejects with an `AbortError`, which ends an awaiting loop; Honey does not report that
|
|
40
|
+
* rejection, and an unawaited one never surfaces as an unhandled rejection.
|
|
41
|
+
*/
|
|
33
42
|
send(event: SSEEvent): Promise<void>
|
|
43
|
+
/** Aborts when the client disconnects, the request times out, or the server shuts down. */
|
|
44
|
+
readonly signal: AbortSignal
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
export type ErrorFormatter = (error: HoneyError, defaultShape: Record<string, unknown>) => Record<string, unknown>
|
|
@@ -55,7 +66,9 @@ function applyPlainOptions(headers: Record<string, string | string[]>, opts?: Re
|
|
|
55
66
|
}
|
|
56
67
|
}
|
|
57
68
|
if (opts?.cookies) {
|
|
58
|
-
|
|
69
|
+
/* append, as `Headers.append` does on the native path: a `set-cookie` in `opts.headers` stays */
|
|
70
|
+
const existing = headers["set-cookie"]
|
|
71
|
+
const cookies: string[] = existing === undefined ? [] : Array.isArray(existing) ? [...existing] : [existing]
|
|
59
72
|
for (const [name, cookieOpts] of Object.entries(opts.cookies)) {
|
|
60
73
|
cookies.push(serializeCookie(name, cookieOpts))
|
|
61
74
|
}
|
|
@@ -63,6 +76,11 @@ function applyPlainOptions(headers: Record<string, string | string[]>, opts?: Re
|
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
78
|
|
|
79
|
+
/** Statuses whose responses never carry a body (Fetch "null body status"). */
|
|
80
|
+
function isNullBodyStatus(status: number): boolean {
|
|
81
|
+
return status === 101 || status === 103 || status === 204 || status === 205 || status === 304
|
|
82
|
+
}
|
|
83
|
+
|
|
66
84
|
/* phantom brands — exist only at type level, never assigned at runtime */
|
|
67
85
|
declare const CONTENT_TYPE: unique symbol
|
|
68
86
|
declare const STATUS_KEY: unique symbol
|
|
@@ -93,6 +111,11 @@ const HTML_HEADERS = { "content-type": "text/html; charset=utf-8" }
|
|
|
93
111
|
const CSV_HEADERS = { "content-type": "text/csv; charset=utf-8" }
|
|
94
112
|
const BINARY_HEADERS = { "content-type": "application/octet-stream" }
|
|
95
113
|
|
|
114
|
+
/** Report a stream producer failure where no logger is wired (a bare `HoneyRes`). */
|
|
115
|
+
function reportToConsole(err: unknown): void {
|
|
116
|
+
console.error("honey: stream producer failed", err)
|
|
117
|
+
}
|
|
118
|
+
|
|
96
119
|
export class HoneyRes {
|
|
97
120
|
protected readonly _nodeOut: boolean
|
|
98
121
|
|
|
@@ -100,12 +123,22 @@ export class HoneyRes {
|
|
|
100
123
|
this._nodeOut = nodeOut
|
|
101
124
|
}
|
|
102
125
|
|
|
126
|
+
/** Signal and error reporting for streams; the per-request res wires `ctx.signal` and the logger. */
|
|
127
|
+
protected _producer(): ProducerOptions {
|
|
128
|
+
return { report: reportToConsole }
|
|
129
|
+
}
|
|
130
|
+
|
|
103
131
|
private _known<CT extends string, SK extends string>(
|
|
104
|
-
|
|
132
|
+
statusCode: number,
|
|
105
133
|
headers: Record<string, string>,
|
|
106
134
|
raw: string | Uint8Array,
|
|
107
135
|
opts?: ResponseOptions,
|
|
108
136
|
): TypedResponse<CT, SK> {
|
|
137
|
+
const status = opts?.status ?? statusCode
|
|
138
|
+
if (isNullBodyStatus(status)) {
|
|
139
|
+
/* `new Response(body, { status: 204 })` throws on the native path; fail the same way everywhere */
|
|
140
|
+
throw new TypeError(`A ${status} response cannot have a body; use ctx.res.noContent() or another status`)
|
|
141
|
+
}
|
|
109
142
|
if (this._nodeOut) {
|
|
110
143
|
if (!opts?.headers && !opts?.cookies) {
|
|
111
144
|
return typed(createHoneyResponse({ headers, raw, status }))
|
|
@@ -181,129 +214,128 @@ export class HoneyRes {
|
|
|
181
214
|
}
|
|
182
215
|
|
|
183
216
|
sse(callback: (stream: SSEStream) => Promise<void>, opts?: SSEOptions): TypedResponse<"text/event-stream", "ok"> {
|
|
184
|
-
const { readable, writable } = new TransformStream()
|
|
185
|
-
const writer = writable.getWriter()
|
|
186
217
|
const encoder = new TextEncoder()
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.
|
|
208
|
-
|
|
209
|
-
.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
})
|
|
230
|
-
}, opts.keepalive)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
void callback(sseStream).then(
|
|
234
|
-
async () => {
|
|
235
|
-
if (closed) return
|
|
236
|
-
closed = true
|
|
237
|
-
if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
|
|
238
|
-
try {
|
|
239
|
-
await writer.close()
|
|
240
|
-
} catch {}
|
|
241
|
-
},
|
|
242
|
-
async () => {
|
|
243
|
-
if (closed) return
|
|
244
|
-
closed = true
|
|
245
|
-
if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
|
|
246
|
-
try {
|
|
247
|
-
await writer.close()
|
|
248
|
-
} catch {}
|
|
249
|
-
},
|
|
250
|
-
)
|
|
218
|
+
const body = producerStream((sink) => {
|
|
219
|
+
const stream: SSEStream = {
|
|
220
|
+
close: () => sink.close(),
|
|
221
|
+
get closed() {
|
|
222
|
+
return sink.closed
|
|
223
|
+
},
|
|
224
|
+
lastEventId: opts?.lastEventId,
|
|
225
|
+
send(event) {
|
|
226
|
+
if (/[\r\n]/.test(event.event)) {
|
|
227
|
+
throw new Error("SSE event name must not contain newlines")
|
|
228
|
+
}
|
|
229
|
+
if (event.id && /[\r\n]/.test(event.id)) {
|
|
230
|
+
throw new Error("SSE id must not contain newlines")
|
|
231
|
+
}
|
|
232
|
+
const dataStr = typeof event.data === "string" ? event.data : JSON.stringify(event.data)
|
|
233
|
+
const dataLines = dataStr
|
|
234
|
+
.split(/\r\n|\r|\n/)
|
|
235
|
+
.map((line) => `data: ${line}`)
|
|
236
|
+
.join("\n")
|
|
237
|
+
let msg = `event: ${event.event}\n${dataLines}\n`
|
|
238
|
+
if (event.id) msg += `id: ${event.id}\n`
|
|
239
|
+
/** SSE retry must be a non-negative integer per spec — skip if invalid */
|
|
240
|
+
if (event.retry !== undefined && Number.isFinite(event.retry) && event.retry >= 0) {
|
|
241
|
+
msg += `retry: ${Math.floor(event.retry)}\n`
|
|
242
|
+
}
|
|
243
|
+
msg += "\n"
|
|
244
|
+
return sink.write(encoder.encode(msg))
|
|
245
|
+
},
|
|
246
|
+
signal: sink.signal,
|
|
247
|
+
}
|
|
248
|
+
if (opts?.defaultRetry !== undefined) {
|
|
249
|
+
void sink.write(encoder.encode(`retry: ${opts.defaultRetry}\n\n`))
|
|
250
|
+
}
|
|
251
|
+
if (opts?.keepalive !== undefined && opts.keepalive > 0) {
|
|
252
|
+
const heartbeat = encoder.encode(": heartbeat\n\n")
|
|
253
|
+
const timer = setInterval(() => {
|
|
254
|
+
void sink.write(heartbeat)
|
|
255
|
+
}, opts.keepalive)
|
|
256
|
+
sink.onEnd(() => clearInterval(timer))
|
|
257
|
+
}
|
|
258
|
+
return callback(stream)
|
|
259
|
+
}, this._producer())
|
|
251
260
|
|
|
261
|
+
/* no `connection: keep-alive`: HTTP/1.1 keeps the connection by default, the header would
|
|
262
|
+
* override a client's `Connection: close`, and HTTP/2 forbids it */
|
|
252
263
|
const headers = new Headers({
|
|
253
264
|
"cache-control": "no-cache",
|
|
254
|
-
connection: "keep-alive",
|
|
255
265
|
"content-type": "text/event-stream",
|
|
256
266
|
})
|
|
257
267
|
applyResponseOptions(headers, opts)
|
|
258
|
-
return typed(new Response(
|
|
268
|
+
return typed(tagStream(new Response(body, { headers, status: opts?.status ?? 200 })))
|
|
259
269
|
}
|
|
260
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Stream a generator's values. A disconnect, `timeout()` or shutdown calls `generator.return()`,
|
|
273
|
+
* so its `finally` runs; a throw mid-stream breaks the body instead of ending it cleanly.
|
|
274
|
+
*/
|
|
261
275
|
generate(
|
|
262
|
-
generator: AsyncGenerator<string, void, unknown> | Generator<string, void, unknown>,
|
|
276
|
+
generator: AsyncGenerator<string | Uint8Array, void, unknown> | Generator<string | Uint8Array, void, unknown>,
|
|
263
277
|
opts?: { contentType?: string; status?: number },
|
|
264
278
|
): TypedResponse<string, "ok"> {
|
|
265
|
-
const encoder = new TextEncoder()
|
|
266
|
-
const gen = generator
|
|
267
|
-
const readable = new ReadableStream({
|
|
268
|
-
async pull(controller) {
|
|
269
|
-
try {
|
|
270
|
-
const { done, value } = await gen.next()
|
|
271
|
-
if (done) {
|
|
272
|
-
controller.close()
|
|
273
|
-
return
|
|
274
|
-
}
|
|
275
|
-
controller.enqueue(encoder.encode(value))
|
|
276
|
-
} catch {
|
|
277
|
-
controller.close()
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
})
|
|
281
279
|
const headers = new Headers({
|
|
282
280
|
"content-type": opts?.contentType ?? "application/octet-stream",
|
|
283
281
|
})
|
|
284
|
-
|
|
282
|
+
const body = generatorStream(generator, this._producer())
|
|
283
|
+
return typed(tagStream(new Response(body, { headers, status: opts?.status ?? 200 })))
|
|
285
284
|
}
|
|
286
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Write the body through a `WritableStream`. The callback starts when the body is first read
|
|
288
|
+
* (never for HEAD); `signal` aborts when the client disconnects, the request times out, or the
|
|
289
|
+
* server shuts down. Returning ends the body after queued writes flush, whether or not the
|
|
290
|
+
* writable was closed; throwing breaks it, even while the callback holds a writer.
|
|
291
|
+
*/
|
|
287
292
|
stream(
|
|
288
|
-
callback: (writable: WritableStream) => Promise<void>,
|
|
293
|
+
callback: (writable: WritableStream, signal: AbortSignal) => Promise<void>,
|
|
289
294
|
opts?: ResponseOptions,
|
|
290
295
|
): TypedResponse<"application/octet-stream", "ok"> {
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
296
|
+
const encoder = new TextEncoder()
|
|
297
|
+
const body = producerStream(async (sink) => {
|
|
298
|
+
/* `size()` runs as each chunk enters the writable's queue: it counts writes not yet flushed */
|
|
299
|
+
let queued = 0
|
|
300
|
+
let flushed = 0
|
|
301
|
+
let onFlushed: (() => void) | null = null
|
|
302
|
+
const writable = new WritableStream<unknown>(
|
|
303
|
+
{
|
|
304
|
+
abort(reason) {
|
|
305
|
+
sink.error(reason)
|
|
306
|
+
},
|
|
307
|
+
close() {
|
|
308
|
+
sink.close()
|
|
309
|
+
},
|
|
310
|
+
async write(chunk) {
|
|
311
|
+
try {
|
|
312
|
+
await sink.write(toBytes(chunk, encoder))
|
|
313
|
+
} finally {
|
|
314
|
+
flushed++
|
|
315
|
+
if (flushed === queued) onFlushed?.()
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
highWaterMark: 1,
|
|
321
|
+
size() {
|
|
322
|
+
queued++
|
|
323
|
+
return 1
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
)
|
|
327
|
+
await callback(writable, sink.signal)
|
|
328
|
+
if (sink.closed) return
|
|
329
|
+
if (flushed < queued) {
|
|
330
|
+
await new Promise<void>((resolve) => {
|
|
331
|
+
onFlushed = resolve
|
|
332
|
+
sink.onEnd(resolve)
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
}, this._producer())
|
|
304
336
|
const headers = new Headers()
|
|
305
337
|
applyResponseOptions(headers, opts)
|
|
306
|
-
return typed(new Response(
|
|
338
|
+
return typed(tagStream(new Response(body, { headers, status: opts?.status ?? 200 })))
|
|
307
339
|
}
|
|
308
340
|
|
|
309
341
|
text<SK extends StatusKey>(statusKey: SK, body: string, opts?: ResponseOptions): TypedResponse<"text/plain", SK> {
|
|
@@ -315,6 +347,14 @@ export class HoneyRes {
|
|
|
315
347
|
}
|
|
316
348
|
}
|
|
317
349
|
|
|
350
|
+
function toBytes(chunk: unknown, encoder: TextEncoder): Uint8Array {
|
|
351
|
+
if (chunk instanceof Uint8Array) return chunk
|
|
352
|
+
if (typeof chunk === "string") return encoder.encode(chunk)
|
|
353
|
+
if (chunk instanceof ArrayBuffer) return new Uint8Array(chunk)
|
|
354
|
+
if (ArrayBuffer.isView(chunk)) return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)
|
|
355
|
+
throw new TypeError("ctx.res.stream() chunks must be strings, ArrayBuffers or typed arrays")
|
|
356
|
+
}
|
|
357
|
+
|
|
318
358
|
export type CustomErrorFormatter = (error: HoneyError, data: Record<string, unknown>) => Record<string, unknown>
|
|
319
359
|
|
|
320
360
|
export function createErrorResponse(
|