@lovrozagar/honey 0.6.8 → 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 +408 -148
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2324 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +140 -0
- package/dist/init.js.map +1 -0
- package/dist/input.js +4 -0
- package/dist/input.js.map +1 -0
- package/dist/invalidate-check.d.ts +11 -0
- package/dist/invalidate-check.d.ts.map +1 -1
- package/dist/invalidate-check.js +163 -0
- package/dist/invalidate-check.js.map +1 -0
- package/dist/invoke-user.d.ts +35 -0
- package/dist/invoke-user.d.ts.map +1 -0
- package/dist/invoke-user.js +73 -0
- package/dist/invoke-user.js.map +1 -0
- package/dist/ip-restrict.d.ts +13 -1
- package/dist/ip-restrict.d.ts.map +1 -1
- package/dist/ip-restrict.js +60 -0
- package/dist/ip-restrict.js.map +1 -0
- package/dist/ip.d.ts +39 -0
- package/dist/ip.d.ts.map +1 -0
- package/dist/ip.js +209 -0
- package/dist/ip.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +113 -0
- package/dist/logger.js.map +1 -0
- package/dist/media-type.d.ts +30 -0
- package/dist/media-type.d.ts.map +1 -0
- package/dist/media-type.js +97 -0
- package/dist/media-type.js.map +1 -0
- package/dist/meta-spec-merge.d.ts +32 -0
- package/dist/meta-spec-merge.d.ts.map +1 -0
- package/dist/meta-spec-merge.js +68 -0
- package/dist/meta-spec-merge.js.map +1 -0
- package/dist/meta-spec.d.ts +16 -3
- package/dist/meta-spec.d.ts.map +1 -1
- package/dist/meta-spec.js +535 -0
- package/dist/meta-spec.js.map +1 -0
- package/dist/middleware.d.ts +23 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +253 -0
- package/dist/middleware.js.map +1 -0
- package/dist/node-request.d.ts +57 -12
- package/dist/node-request.d.ts.map +1 -1
- package/dist/node-request.js +407 -0
- package/dist/node-request.js.map +1 -0
- package/dist/node.d.ts +20 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +396 -0
- package/dist/node.js.map +1 -0
- package/dist/openapi/collect.d.ts +6 -3
- package/dist/openapi/collect.d.ts.map +1 -1
- package/dist/openapi/collect.js +30 -0
- package/dist/openapi/collect.js.map +1 -0
- package/dist/openapi/docs-page.d.ts +42 -0
- package/dist/openapi/docs-page.d.ts.map +1 -0
- package/dist/openapi/docs-page.js +80 -0
- package/dist/openapi/docs-page.js.map +1 -0
- package/dist/openapi/document.d.ts +4 -0
- package/dist/openapi/document.d.ts.map +1 -1
- package/dist/openapi/document.js +785 -0
- package/dist/openapi/document.js.map +1 -0
- package/dist/openapi/json-schema-slot.d.ts +7 -0
- package/dist/openapi/json-schema-slot.d.ts.map +1 -1
- package/dist/openapi/json-schema-slot.js +38 -0
- package/dist/openapi/json-schema-slot.js.map +1 -0
- package/dist/openapi/register.d.ts.map +1 -1
- package/dist/openapi/register.js +20 -0
- package/dist/openapi/register.js.map +1 -0
- package/dist/openapi/scalar.d.ts +3 -1
- package/dist/openapi/scalar.d.ts.map +1 -1
- package/dist/openapi/scalar.js +21 -0
- package/dist/openapi/scalar.js.map +1 -0
- package/dist/openapi/serve.d.ts +9 -0
- package/dist/openapi/serve.d.ts.map +1 -0
- package/dist/openapi/serve.js +36 -0
- package/dist/openapi/serve.js.map +1 -0
- package/dist/openapi/spec-factory.d.ts +30 -1
- package/dist/openapi/spec-factory.d.ts.map +1 -1
- package/dist/openapi/spec-factory.js +52 -0
- package/dist/openapi/spec-factory.js.map +1 -0
- package/dist/openapi/spec.d.ts +14 -5
- package/dist/openapi/spec.d.ts.map +1 -1
- package/dist/openapi/spec.js +50 -0
- package/dist/openapi/spec.js.map +1 -0
- package/dist/openapi/swagger.d.ts +3 -1
- package/dist/openapi/swagger.d.ts.map +1 -1
- package/dist/openapi/swagger.js +22 -0
- package/dist/openapi/swagger.js.map +1 -0
- package/dist/pattern.d.ts +71 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +239 -0
- package/dist/pattern.js.map +1 -0
- package/dist/peer.d.ts +17 -0
- package/dist/peer.d.ts.map +1 -0
- package/dist/peer.js +50 -0
- package/dist/peer.js.map +1 -0
- package/dist/plugin.d.ts +67 -16
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +649 -0
- package/dist/plugin.js.map +1 -0
- package/dist/powered-by.d.ts.map +1 -1
- package/dist/powered-by.js +11 -0
- package/dist/powered-by.js.map +1 -0
- package/dist/pretty-json.d.ts.map +1 -1
- package/dist/pretty-json.js +47 -0
- package/dist/pretty-json.js.map +1 -0
- package/dist/producer-stream.d.ts +47 -0
- package/dist/producer-stream.d.ts.map +1 -0
- package/dist/producer-stream.js +267 -0
- package/dist/producer-stream.js.map +1 -0
- package/dist/proxy.d.ts +47 -8
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +274 -0
- package/dist/proxy.js.map +1 -0
- package/dist/python-type-emitter.d.ts +14 -1
- package/dist/python-type-emitter.d.ts.map +1 -1
- package/dist/python-type-emitter.js +157 -0
- package/dist/python-type-emitter.js.map +1 -0
- package/dist/realtime/bus.d.ts +17 -1
- package/dist/realtime/bus.d.ts.map +1 -1
- package/dist/realtime/bus.js +112 -0
- package/dist/realtime/bus.js.map +1 -0
- package/dist/realtime/register.d.ts +3 -0
- package/dist/realtime/register.d.ts.map +1 -0
- package/dist/realtime/register.js +10 -0
- package/dist/realtime/register.js.map +1 -0
- package/dist/realtime/route.d.ts +65 -26
- package/dist/realtime/route.d.ts.map +1 -1
- package/dist/realtime/route.js +72 -0
- package/dist/realtime/route.js.map +1 -0
- package/dist/realtime/server.d.ts +43 -0
- package/dist/realtime/server.d.ts.map +1 -0
- package/dist/realtime/server.js +255 -0
- package/dist/realtime/server.js.map +1 -0
- package/dist/request-id.d.ts +7 -0
- package/dist/request-id.d.ts.map +1 -1
- package/dist/request-id.js +16 -0
- package/dist/request-id.js.map +1 -0
- package/dist/request-limits.d.ts +3 -0
- package/dist/request-limits.d.ts.map +1 -0
- package/dist/request-limits.js +3 -0
- package/dist/request-limits.js.map +1 -0
- package/dist/request-path.d.ts +33 -0
- package/dist/request-path.d.ts.map +1 -0
- package/dist/request-path.js +178 -0
- package/dist/request-path.js.map +1 -0
- package/dist/request-to-curl.d.ts +7 -1
- package/dist/request-to-curl.d.ts.map +1 -1
- package/dist/request-to-curl.js +48 -0
- package/dist/request-to-curl.js.map +1 -0
- package/dist/response.d.ts +25 -2
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +313 -0
- package/dist/response.js.map +1 -0
- package/dist/rust-type-emitter.d.ts +14 -2
- package/dist/rust-type-emitter.d.ts.map +1 -1
- package/dist/rust-type-emitter.js +463 -0
- package/dist/rust-type-emitter.js.map +1 -0
- package/dist/secure-headers.d.ts.map +1 -1
- package/dist/secure-headers.js +53 -0
- package/dist/secure-headers.js.map +1 -0
- package/dist/serve-register.d.ts +4 -0
- package/dist/serve-register.d.ts.map +1 -1
- package/dist/serve-register.js +9 -0
- package/dist/serve-register.js.map +1 -0
- package/dist/serve-slot.d.ts +3 -0
- package/dist/serve-slot.d.ts.map +1 -1
- package/dist/serve-slot.js +52 -0
- package/dist/serve-slot.js.map +1 -0
- package/dist/serve.d.ts +18 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +157 -0
- package/dist/serve.js.map +1 -0
- package/dist/server-timing.d.ts.map +1 -1
- package/dist/server-timing.js +51 -0
- package/dist/server-timing.js.map +1 -0
- package/dist/static.d.ts +9 -0
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +102 -0
- package/dist/static.js.map +1 -0
- package/dist/telemetry/otel.d.ts +2 -0
- package/dist/telemetry/otel.d.ts.map +1 -1
- package/dist/telemetry/otel.js +135 -0
- package/dist/telemetry/otel.js.map +1 -0
- package/dist/testing.d.ts +12 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +124 -0
- package/dist/testing.js.map +1 -0
- package/dist/timeout.d.ts +6 -0
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +36 -0
- package/dist/timeout.js.map +1 -0
- package/dist/tree.d.ts +133 -36
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +437 -0
- package/dist/tree.js.map +1 -0
- package/dist/trust.d.ts +21 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +100 -0
- package/dist/trust.js.map +1 -0
- package/dist/ts-type-emitter.d.ts.map +1 -1
- package/dist/ts-type-emitter.js +150 -0
- package/dist/ts-type-emitter.js.map +1 -0
- package/dist/type-emitter.d.ts +16 -2
- package/dist/type-emitter.d.ts.map +1 -1
- package/dist/type-emitter.js +729 -0
- package/dist/type-emitter.js.map +1 -0
- package/dist/type-extractor.d.ts +8 -4
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +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 +1887 -1756
- package/src/init.ts +40 -28
- package/src/invalidate-check.ts +42 -4
- package/src/invoke-user.ts +95 -0
- package/src/ip-restrict.ts +47 -161
- package/src/ip.ts +194 -0
- package/src/logger.ts +65 -36
- package/src/media-type.ts +104 -0
- package/src/meta-spec-merge.ts +80 -0
- package/src/meta-spec.ts +0 -0
- package/src/middleware.ts +112 -12
- package/src/node-request.ts +242 -52
- package/src/node.ts +318 -75
- package/src/openapi/collect.ts +19 -44
- package/src/openapi/docs-page.ts +89 -0
- package/src/openapi/document.ts +503 -224
- package/src/openapi/json-schema-slot.ts +40 -0
- package/src/openapi/register.ts +7 -2
- package/src/openapi/scalar.ts +11 -10
- package/src/openapi/serve.ts +39 -0
- package/src/openapi/spec-factory.ts +50 -1
- package/src/openapi/spec.ts +53 -30
- package/src/openapi/swagger.ts +12 -11
- package/src/pattern.ts +245 -0
- package/src/peer.ts +53 -0
- package/src/plugin.ts +270 -185
- package/src/powered-by.ts +2 -2
- package/src/pretty-json.ts +25 -9
- package/src/producer-stream.ts +296 -0
- package/src/proxy.ts +253 -55
- package/src/python-type-emitter.ts +68 -56
- package/src/realtime/bus.ts +56 -9
- package/src/realtime/register.ts +13 -0
- package/src/realtime/route.ts +129 -80
- package/src/realtime/server.ts +298 -0
- package/src/request-id.ts +13 -3
- package/src/request-limits.ts +2 -0
- package/src/request-path.ts +165 -0
- package/src/request-to-curl.ts +28 -7
- package/src/response.ts +143 -103
- package/src/rust-type-emitter.ts +265 -170
- package/src/secure-headers.ts +8 -4
- package/src/serve-register.ts +4 -0
- package/src/serve-slot.ts +38 -0
- package/src/serve.ts +113 -25
- package/src/server-timing.ts +11 -7
- package/src/static.ts +85 -32
- package/src/telemetry/otel.ts +29 -7
- package/src/testing.ts +76 -14
- package/src/timeout.ts +15 -7
- package/src/tree.ts +405 -333
- package/src/trust.ts +117 -0
- package/src/ts-type-emitter.ts +18 -12
- package/src/type-emitter.ts +227 -89
- package/src/type-extractor.ts +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/crypto.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const encoder = new TextEncoder()
|
|
2
|
-
|
|
3
1
|
let cachedKey: CryptoKey | null = null
|
|
4
2
|
|
|
5
3
|
async function getKey(): Promise<CryptoKey> {
|
|
@@ -9,8 +7,32 @@ async function getKey(): Promise<CryptoKey> {
|
|
|
9
7
|
return cachedKey
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Strings are compared by UTF-16 code unit, not by their UTF-8 encoding: UTF-8
|
|
12
|
+
* replaces every lone surrogate with U+FFFD, so two different strings would
|
|
13
|
+
* compare equal.
|
|
14
|
+
*/
|
|
15
|
+
function toBytes(value: string | Uint8Array): Uint8Array<ArrayBuffer> {
|
|
16
|
+
if (typeof value !== "string") return new Uint8Array(value)
|
|
17
|
+
const bytes = new Uint8Array(value.length * 2)
|
|
18
|
+
for (let i = 0; i < value.length; i++) {
|
|
19
|
+
const unit = value.charCodeAt(i)
|
|
20
|
+
bytes[i * 2] = unit >> 8
|
|
21
|
+
bytes[i * 2 + 1] = unit & 0xff
|
|
22
|
+
}
|
|
23
|
+
return bytes
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Constant-time equality. Both sides are MACed with a per-process random key and
|
|
28
|
+
* the MACs are compared by `crypto.subtle.verify`, so neither content nor length
|
|
29
|
+
* leaks through timing.
|
|
30
|
+
*/
|
|
31
|
+
export async function timingSafeEqual(a: string | Uint8Array, b: string | Uint8Array): Promise<boolean> {
|
|
32
|
+
/* A string never equals bytes: tag the kind so "ab" and its code units differ. */
|
|
33
|
+
const aIsString = typeof a === "string"
|
|
34
|
+
if (aIsString !== (typeof b === "string")) return false
|
|
13
35
|
const key = await getKey()
|
|
14
|
-
const sig = await crypto.subtle.sign("HMAC", key,
|
|
15
|
-
return crypto.subtle.verify("HMAC", key, sig,
|
|
36
|
+
const sig = await crypto.subtle.sign("HMAC", key, toBytes(a))
|
|
37
|
+
return crypto.subtle.verify("HMAC", key, sig, toBytes(b))
|
|
16
38
|
}
|
package/src/csrf.ts
CHANGED
|
@@ -4,11 +4,15 @@ import type { MiddlewareFn } from "./middleware.ts"
|
|
|
4
4
|
import { EK, SK } from "./types.ts"
|
|
5
5
|
|
|
6
6
|
type CSRFOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Cross-origin callers allowed to make unsafe requests, matched against the
|
|
9
|
+
* full `Origin` (`https://app.example.com`). Same-origin requests never need
|
|
10
|
+
* to be listed.
|
|
11
|
+
*/
|
|
7
12
|
origin?: ((origin: string) => boolean) | string | string[]
|
|
8
13
|
}
|
|
9
14
|
|
|
10
|
-
const
|
|
11
|
-
const formContentTypeRe = /^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/i
|
|
15
|
+
const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"])
|
|
12
16
|
|
|
13
17
|
function matchOrigin(origin: string, config: CSRFOptions["origin"]): boolean {
|
|
14
18
|
if (config === undefined) return false
|
|
@@ -17,37 +21,65 @@ function matchOrigin(origin: string, config: CSRFOptions["origin"]): boolean {
|
|
|
17
21
|
return config(origin)
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
/** Host of the request as the client addressed it: `Host`, else the URL's host. */
|
|
25
|
+
function requestHost(req: Request): string | null {
|
|
26
|
+
const host = req.headers.get("host")
|
|
27
|
+
if (host !== null && host !== "") return host.toLowerCase()
|
|
28
|
+
try {
|
|
29
|
+
return new URL(req.url).host.toLowerCase()
|
|
30
|
+
} catch {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
}
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
function originHost(origin: string): string | null {
|
|
36
|
+
if (origin === "null") return null
|
|
37
|
+
try {
|
|
38
|
+
return new URL(origin).host.toLowerCase()
|
|
39
|
+
} catch {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
25
43
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
44
|
+
function reject(): never {
|
|
45
|
+
throw new HoneyError({ errorKey: EK.forbidden, status: SK.forbidden })
|
|
46
|
+
}
|
|
29
47
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Rejects cross-origin unsafe requests, whatever their content type.
|
|
50
|
+
*
|
|
51
|
+
* 1. `GET`, `HEAD` and `OPTIONS` pass.
|
|
52
|
+
* 2. With `Sec-Fetch-Site` (every current browser sends it): `same-origin` and
|
|
53
|
+
* `none` (user-initiated) pass. `same-site` and `cross-site` pass only for an
|
|
54
|
+
* allow-listed `Origin`. A sibling subdomain is same-site but not trusted.
|
|
55
|
+
* 3. Without it: no `Origin` passes (curl, server-to-server, old clients that
|
|
56
|
+
* cannot be driven cross-site), an `Origin` whose host equals `Host` passes,
|
|
57
|
+
* an allow-listed `Origin` passes, anything else is rejected.
|
|
58
|
+
*
|
|
59
|
+
* Failure is 403 `forbidden`. This is the algorithm of Go's
|
|
60
|
+
* `http.CrossOriginProtection`.
|
|
61
|
+
*/
|
|
62
|
+
export function csrf(options?: CSRFOptions): MiddlewareFn<{ req: Request }, {}> {
|
|
63
|
+
const allow = options?.origin
|
|
34
64
|
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
return next()
|
|
39
|
-
}
|
|
65
|
+
const mw: MiddlewareFn<{ req: Request }, {}> = (ctx, next) => {
|
|
66
|
+
const req = ctx.req
|
|
67
|
+
if (SAFE_METHODS.has(req.method)) return next()
|
|
40
68
|
|
|
41
|
-
/* fall back to Origin header */
|
|
42
69
|
const origin = req.headers.get("origin")
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
const site = req.headers.get("sec-fetch-site")
|
|
71
|
+
|
|
72
|
+
if (site !== null) {
|
|
73
|
+
if (site === "same-origin" || site === "none") return next()
|
|
74
|
+
if (origin !== null && matchOrigin(origin, allow)) return next()
|
|
75
|
+
return reject()
|
|
45
76
|
}
|
|
46
77
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
if (origin === null) return next()
|
|
79
|
+
const host = originHost(origin)
|
|
80
|
+
if (host !== null && host === requestHost(req)) return next()
|
|
81
|
+
if (matchOrigin(origin, allow)) return next()
|
|
82
|
+
return reject()
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
return namedMiddleware("csrf", mw)
|
package/src/curl-logger.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { namedMiddleware } from "./middleware.ts"
|
|
2
2
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
3
3
|
import type { LoggerInstance } from "./logger.ts"
|
|
4
|
+
import { shellQuote } from "./request-to-curl.ts"
|
|
4
5
|
|
|
5
6
|
type BodyOmittedReason = "content-type" | "disabled" | "missing" | "read-error" | "too-large"
|
|
6
7
|
|
|
@@ -15,6 +16,9 @@ type CurlLogData = {
|
|
|
15
16
|
status: number
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
/** What `skip` sees: everything known before the curl command is built. */
|
|
20
|
+
type CurlSkipData = Pick<CurlLogData, "duration" | "method" | "path" | "requestId" | "status">
|
|
21
|
+
|
|
18
22
|
type CurlLoggerBodyOptions = {
|
|
19
23
|
allowContentTypes?: string[]
|
|
20
24
|
maxBytes?: number
|
|
@@ -24,17 +28,46 @@ type CurlLoggerOptions = {
|
|
|
24
28
|
body?: boolean | CurlLoggerBodyOptions
|
|
25
29
|
instance?: LoggerInstance
|
|
26
30
|
log?: (data: CurlLogData) => void
|
|
31
|
+
/**
|
|
32
|
+
* Return the value to log, or null to drop the header. Replaces the default,
|
|
33
|
+
* which masks `authorization`, `proxy-authorization`, `cookie`, `set-cookie`
|
|
34
|
+
* and API-key/token headers. Call `defaultRedactHeader` to keep it and add more.
|
|
35
|
+
*/
|
|
27
36
|
redactHeader?: (name: string, value: string) => string | null
|
|
37
|
+
/** Same for query parameters. The default masks token-, key-, secret- and password-like names. */
|
|
28
38
|
redactQueryParam?: (name: string, value: string) => string | null
|
|
29
|
-
|
|
39
|
+
/** Runs before the curl command is built; returning true skips all of that work. */
|
|
40
|
+
skip?: (data: CurlSkipData) => boolean
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
const DEFAULT_BODY_CONTENT_TYPES = ["application/json", "application/x-www-form-urlencoded", "application/xml", "text/"]
|
|
33
44
|
|
|
34
45
|
const DEFAULT_MAX_BODY_BYTES = 16_384
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
const REDACTED = "[REDACTED]"
|
|
48
|
+
|
|
49
|
+
const SENSITIVE_HEADERS = new Set([
|
|
50
|
+
"authorization",
|
|
51
|
+
"cookie",
|
|
52
|
+
"proxy-authorization",
|
|
53
|
+
"set-cookie",
|
|
54
|
+
"x-api-key",
|
|
55
|
+
"x-auth-token",
|
|
56
|
+
"x-csrf-token",
|
|
57
|
+
"x-xsrf-token",
|
|
58
|
+
])
|
|
59
|
+
const SENSITIVE_HEADER_RE = /(?:^|-)(?:token|secret|api-?key|password|session)(?:$|-)/i
|
|
60
|
+
const SENSITIVE_PARAM_RE =
|
|
61
|
+
/(?:^|[_.-])(?:access_?token|refresh_?token|id_?token|token|secret|client_?secret|password|passwd|pwd|api_?key|apikey|key|sig|signature|auth|code|session|jwt)$/i
|
|
62
|
+
|
|
63
|
+
function defaultRedactHeader(name: string, value: string): string | null {
|
|
64
|
+
const lower = name.toLowerCase()
|
|
65
|
+
if (SENSITIVE_HEADERS.has(lower) || SENSITIVE_HEADER_RE.test(lower)) return REDACTED
|
|
66
|
+
return value
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function defaultRedactQueryParam(name: string, value: string): string | null {
|
|
70
|
+
return SENSITIVE_PARAM_RE.test(name) ? REDACTED : value
|
|
38
71
|
}
|
|
39
72
|
|
|
40
73
|
function defaultLog(data: CurlLogData): void {
|
|
@@ -43,175 +76,183 @@ function defaultLog(data: CurlLogData): void {
|
|
|
43
76
|
|
|
44
77
|
function shouldIncludeBody(contentType: string | null, allowContentTypes: string[]): boolean {
|
|
45
78
|
if (contentType === null || contentType.length === 0) return false
|
|
46
|
-
|
|
79
|
+
const lower = contentType.toLowerCase()
|
|
80
|
+
return allowContentTypes.some((allowed) => lower.startsWith(allowed.toLowerCase()))
|
|
47
81
|
}
|
|
48
82
|
|
|
49
|
-
function redactUrl(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
83
|
+
function redactUrl(rawUrl: string, redactQueryParam: (name: string, value: string) => string | null): string {
|
|
84
|
+
let url: URL
|
|
85
|
+
try {
|
|
86
|
+
url = new URL(rawUrl)
|
|
87
|
+
} catch {
|
|
88
|
+
/* a malformed Host (Node builds the URL from it) must not fail the request */
|
|
89
|
+
return rawUrl.split("?")[0] ?? rawUrl
|
|
90
|
+
}
|
|
91
|
+
if (url.search.length === 0) return url.toString()
|
|
92
|
+
const params = new URLSearchParams()
|
|
93
|
+
for (const [name, value] of url.searchParams) {
|
|
56
94
|
const nextValue = redactQueryParam(name, value)
|
|
57
|
-
if (nextValue
|
|
58
|
-
redacted.searchParams.append(name, nextValue)
|
|
95
|
+
if (nextValue !== null) params.append(name, nextValue)
|
|
59
96
|
}
|
|
60
|
-
|
|
61
|
-
return
|
|
97
|
+
url.search = params.toString()
|
|
98
|
+
return url.toString()
|
|
62
99
|
}
|
|
63
100
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
type BodyResult = { body: string | null; omittedReason: BodyOmittedReason | null }
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A second, independent reader of the request body. On Node the request is
|
|
105
|
+
* re-pointed at one branch of a tee (the same hook `bodyLimit` uses) rather
|
|
106
|
+
* than `clone()`d, so later body readers keep working.
|
|
107
|
+
*/
|
|
108
|
+
function teeBody(request: Request): ReadableStream<Uint8Array> | null {
|
|
109
|
+
const body = request.body
|
|
110
|
+
if (body === null) return null
|
|
111
|
+
const replaceBody = (request as unknown as Record<symbol, unknown>)[Symbol.for("honey.replaceBody")]
|
|
112
|
+
if (typeof replaceBody === "function") {
|
|
113
|
+
const [forRequest, forLog] = body.tee()
|
|
114
|
+
;(replaceBody as (stream: ReadableStream<Uint8Array>) => void).call(request, forRequest)
|
|
115
|
+
return forLog
|
|
73
116
|
}
|
|
117
|
+
return request.clone().body
|
|
118
|
+
}
|
|
74
119
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
return { body: null, omittedReason: "too-large" }
|
|
80
|
-
}
|
|
81
|
-
}
|
|
120
|
+
/** Never rejects: a body that cannot be read is logged as omitted. */
|
|
121
|
+
async function readBodyWithinLimit(request: Request, maxBytes: number): Promise<BodyResult> {
|
|
122
|
+
try {
|
|
123
|
+
if (request.body === null) return { body: null, omittedReason: "missing" }
|
|
82
124
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
const contentLength = request.headers.get("content-length")
|
|
126
|
+
if (contentLength !== null) {
|
|
127
|
+
const parsedLength = Number.parseInt(contentLength, 10)
|
|
128
|
+
if (!Number.isNaN(parsedLength) && parsedLength > maxBytes) {
|
|
129
|
+
return { body: null, omittedReason: "too-large" }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
87
132
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let totalBytes = 0
|
|
133
|
+
const stream = teeBody(request)
|
|
134
|
+
if (stream === null) return { body: null, omittedReason: "missing" }
|
|
91
135
|
|
|
92
|
-
|
|
136
|
+
const reader = stream.getReader()
|
|
137
|
+
const chunks: Uint8Array[] = []
|
|
138
|
+
let totalBytes = 0
|
|
93
139
|
while (true) {
|
|
94
140
|
const { done, value } = await reader.read()
|
|
95
141
|
if (done) break
|
|
96
142
|
totalBytes += value.byteLength
|
|
97
143
|
if (totalBytes > maxBytes) {
|
|
98
|
-
await reader.cancel()
|
|
144
|
+
await reader.cancel().catch(() => {})
|
|
99
145
|
return { body: null, omittedReason: "too-large" }
|
|
100
146
|
}
|
|
101
147
|
chunks.push(value)
|
|
102
148
|
}
|
|
149
|
+
|
|
150
|
+
const bytes = new Uint8Array(totalBytes)
|
|
151
|
+
let offset = 0
|
|
152
|
+
for (const chunk of chunks) {
|
|
153
|
+
bytes.set(chunk, offset)
|
|
154
|
+
offset += chunk.byteLength
|
|
155
|
+
}
|
|
156
|
+
return { body: new TextDecoder().decode(bytes), omittedReason: null }
|
|
103
157
|
} catch {
|
|
104
158
|
return { body: null, omittedReason: "read-error" }
|
|
105
159
|
}
|
|
160
|
+
}
|
|
106
161
|
|
|
107
|
-
|
|
108
|
-
|
|
162
|
+
type ResolvedOptions = {
|
|
163
|
+
redactHeader: (name: string, value: string) => string | null
|
|
164
|
+
redactQueryParam: (name: string, value: string) => string | null
|
|
165
|
+
}
|
|
109
166
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
167
|
+
function formatCurl(request: Request, body: BodyResult | null, opts: ResolvedOptions): string {
|
|
168
|
+
const parts: string[] = ["curl", "-X", shellQuote(request.method)]
|
|
169
|
+
for (const [name, value] of request.headers.entries()) {
|
|
170
|
+
const redactedValue = opts.redactHeader(name, value)
|
|
171
|
+
if (redactedValue === null) continue
|
|
172
|
+
parts.push("-H", shellQuote(`${name}: ${redactedValue}`))
|
|
113
173
|
}
|
|
174
|
+
if (body !== null && body.body !== null) parts.push("--data-raw", shellQuote(body.body))
|
|
175
|
+
parts.push(shellQuote(redactUrl(request.url, opts.redactQueryParam)))
|
|
176
|
+
return parts.join(" ")
|
|
177
|
+
}
|
|
114
178
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
179
|
+
/** Start reading the body (if logged) now, before a handler consumes it. */
|
|
180
|
+
function startBodyRead(request: Request, body: CurlLoggerOptions["body"]): Promise<BodyResult> {
|
|
181
|
+
if (body !== true && typeof body !== "object") {
|
|
182
|
+
return Promise.resolve({ body: null, omittedReason: "disabled" })
|
|
118
183
|
}
|
|
184
|
+
const bodyOptions = typeof body === "object" ? body : {}
|
|
185
|
+
const allowContentTypes = bodyOptions.allowContentTypes ?? DEFAULT_BODY_CONTENT_TYPES
|
|
186
|
+
if (!shouldIncludeBody(request.headers.get("content-type"), allowContentTypes)) {
|
|
187
|
+
return Promise.resolve({ body: null, omittedReason: request.body === null ? "missing" : "content-type" })
|
|
188
|
+
}
|
|
189
|
+
return readBodyWithinLimit(request, bodyOptions.maxBytes ?? DEFAULT_MAX_BODY_BYTES)
|
|
119
190
|
}
|
|
120
191
|
|
|
121
192
|
async function buildCurlLogData(
|
|
122
193
|
request: Request,
|
|
123
194
|
options?: Pick<CurlLoggerOptions, "body" | "redactHeader" | "redactQueryParam">,
|
|
124
195
|
): Promise<Pick<CurlLogData, "bodyIncluded" | "bodyOmittedReason" | "curl">> {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const redactedValue = options?.redactHeader !== undefined ? options.redactHeader(name, value) : value
|
|
129
|
-
if (redactedValue === null) continue
|
|
130
|
-
parts.push(`-H '${shellEscape(name)}: ${shellEscape(redactedValue)}'`)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
let bodyIncluded = false
|
|
134
|
-
let bodyOmittedReason: BodyOmittedReason | null = null
|
|
135
|
-
|
|
136
|
-
if (options?.body === true || typeof options?.body === "object") {
|
|
137
|
-
const bodyOptions = typeof options.body === "object" ? options.body : {}
|
|
138
|
-
const allowContentTypes = bodyOptions.allowContentTypes ?? DEFAULT_BODY_CONTENT_TYPES
|
|
139
|
-
const contentType = request.headers.get("content-type")
|
|
140
|
-
|
|
141
|
-
if (shouldIncludeBody(contentType, allowContentTypes) === false) {
|
|
142
|
-
bodyOmittedReason = request.body === null ? "missing" : "content-type"
|
|
143
|
-
} else {
|
|
144
|
-
const result = await readBodyWithinLimit(request, bodyOptions.maxBytes ?? DEFAULT_MAX_BODY_BYTES)
|
|
145
|
-
|
|
146
|
-
if (result.body === null) {
|
|
147
|
-
bodyOmittedReason = result.omittedReason
|
|
148
|
-
} else {
|
|
149
|
-
bodyIncluded = true
|
|
150
|
-
parts.push(`--data-raw '${shellEscape(result.body)}'`)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
bodyOmittedReason = "disabled"
|
|
196
|
+
const resolved: ResolvedOptions = {
|
|
197
|
+
redactHeader: options?.redactHeader ?? defaultRedactHeader,
|
|
198
|
+
redactQueryParam: options?.redactQueryParam ?? defaultRedactQueryParam,
|
|
155
199
|
}
|
|
156
|
-
|
|
157
|
-
parts.push(`'${shellEscape(redactUrl(new URL(request.url), options?.redactQueryParam))}'`)
|
|
158
|
-
|
|
200
|
+
const body = await startBodyRead(request, options?.body)
|
|
159
201
|
return {
|
|
160
|
-
bodyIncluded,
|
|
161
|
-
bodyOmittedReason,
|
|
162
|
-
curl:
|
|
202
|
+
bodyIncluded: body.body !== null,
|
|
203
|
+
bodyOmittedReason: body.omittedReason,
|
|
204
|
+
curl: formatCurl(request, body, resolved),
|
|
163
205
|
}
|
|
164
206
|
}
|
|
165
207
|
|
|
166
208
|
function curlLogger(options?: CurlLoggerOptions): MiddlewareFn<{ path: string; req: Request }, {}> {
|
|
167
209
|
const log = options?.log ?? defaultLog
|
|
168
210
|
const skip = options?.skip
|
|
211
|
+
const resolved: ResolvedOptions = {
|
|
212
|
+
redactHeader: options?.redactHeader ?? defaultRedactHeader,
|
|
213
|
+
redactQueryParam: options?.redactQueryParam ?? defaultRedactQueryParam,
|
|
214
|
+
}
|
|
169
215
|
|
|
170
216
|
return namedMiddleware("curlLogger", async (ctx, next) => {
|
|
171
217
|
const start = performance.now()
|
|
172
|
-
const
|
|
218
|
+
const request = ctx.req
|
|
219
|
+
const method = request.method
|
|
173
220
|
const path = ctx.path
|
|
174
221
|
const rid = ((ctx as Record<string, unknown>)["requestId"] as string | null) ?? null
|
|
175
|
-
|
|
222
|
+
/* the only work before `next()`: the body must be teed before a handler reads it */
|
|
223
|
+
const bodyPromise = startBodyRead(request, options?.body)
|
|
176
224
|
|
|
177
225
|
const response = await next()
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const data: CurlLogData = {
|
|
182
|
-
...curlData,
|
|
183
|
-
duration,
|
|
226
|
+
const skipData: CurlSkipData = {
|
|
227
|
+
duration: performance.now() - start,
|
|
184
228
|
method,
|
|
185
229
|
path,
|
|
186
230
|
requestId: rid,
|
|
187
231
|
status: response.status,
|
|
188
232
|
}
|
|
189
233
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
} else {
|
|
209
|
-
log(data)
|
|
234
|
+
/* a failing redact callback, skip or sink never turns the response into a 500 */
|
|
235
|
+
try {
|
|
236
|
+
if (skip?.(skipData)) return response
|
|
237
|
+
const body = await bodyPromise
|
|
238
|
+
const data: CurlLogData = {
|
|
239
|
+
...skipData,
|
|
240
|
+
bodyIncluded: body.body !== null,
|
|
241
|
+
bodyOmittedReason: body.omittedReason,
|
|
242
|
+
curl: formatCurl(request, body, resolved),
|
|
243
|
+
}
|
|
244
|
+
if (options?.instance) {
|
|
245
|
+
options.instance.info({ ...data }, "request curl")
|
|
246
|
+
} else {
|
|
247
|
+
log(data)
|
|
248
|
+
}
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.error("curlLogger: failed to log request", err)
|
|
210
251
|
}
|
|
211
252
|
|
|
212
253
|
return response
|
|
213
254
|
})
|
|
214
255
|
}
|
|
215
256
|
|
|
216
|
-
export { buildCurlLogData, curlLogger }
|
|
217
|
-
export type { BodyOmittedReason, CurlLogData, CurlLoggerBodyOptions, CurlLoggerOptions }
|
|
257
|
+
export { buildCurlLogData, curlLogger, defaultRedactHeader, defaultRedactQueryParam }
|
|
258
|
+
export type { BodyOmittedReason, CurlLogData, CurlLoggerBodyOptions, CurlLoggerOptions, CurlSkipData }
|
package/src/dict.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An object for keys that come from request data: search params, form fields, header views,
|
|
3
|
+
* validation field maps. It has no prototype, so a key named `__proto__`, `constructor` or
|
|
4
|
+
* `toString` is plain data — it cannot hit `Object.prototype`, replace the object's prototype,
|
|
5
|
+
* or turn a missing key into a function.
|
|
6
|
+
*
|
|
7
|
+
* Read keys with `key in obj`, `obj[key] !== undefined` or `Object.hasOwn(obj, key)`; the
|
|
8
|
+
* object has no `hasOwnProperty` method of its own.
|
|
9
|
+
*/
|
|
10
|
+
export function dict<T>(): Record<string, T> {
|
|
11
|
+
return Object.create(null) as Record<string, T>
|
|
12
|
+
}
|
package/src/error.ts
CHANGED
|
@@ -23,8 +23,10 @@ export class HoneyError extends Error {
|
|
|
23
23
|
this.data = opts.data
|
|
24
24
|
this.errorKey = opts.errorKey
|
|
25
25
|
this.headers = opts.headers
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/* an unknown status key must not turn an error into a 200 — it is a 500 */
|
|
27
|
+
const known = Object.hasOwn(statusKeyToCode, opts.status)
|
|
28
|
+
this.statusKey = known ? opts.status : "internal_server_error"
|
|
29
|
+
this.status = known ? statusKeyToCode[opts.status] : 500
|
|
28
30
|
this.fields = opts.fields ?? EMPTY_FIELDS
|
|
29
31
|
this.vars = opts.vars
|
|
30
32
|
}
|
package/src/etag.ts
CHANGED
|
@@ -1,22 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bodyKind, rawBodyOf } from "./body-kind.ts"
|
|
2
2
|
import { replaceResponse } from "./honey-response.ts"
|
|
3
|
+
import { namedMiddleware } from "./middleware.ts"
|
|
3
4
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
4
5
|
|
|
5
6
|
type ETagOptions = {
|
|
6
7
|
weak?: boolean
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let
|
|
15
|
-
for (let i = 0; i <
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
const encoder = new TextEncoder()
|
|
11
|
+
|
|
12
|
+
/** Base64url of the first 16 bytes of SHA-256: collision-resistant enough for a strong validator. */
|
|
13
|
+
async function digest(data: Uint8Array<ArrayBuffer>): Promise<string> {
|
|
14
|
+
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", data)).subarray(0, 16)
|
|
15
|
+
let binary = ""
|
|
16
|
+
for (let i = 0; i < hash.length; i++) binary += String.fromCharCode(hash[i])
|
|
17
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ENTITY_TAG_RE = /(?:W\/)?"[^"]*"/g
|
|
21
|
+
|
|
22
|
+
/** Weak comparison (RFC 9110 §8.8.3.2): opaque tags match regardless of the `W/` prefix. */
|
|
23
|
+
function opaque(tag: string): string {
|
|
24
|
+
return tag.startsWith("W/") ? tag.slice(2) : tag
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** RFC 9110 §13.1.2: `*` or a list of entity tags, compared weakly. */
|
|
28
|
+
export function ifNoneMatchHits(header: string | null, etag: string): boolean {
|
|
29
|
+
if (header === null) return false
|
|
30
|
+
if (header.trim() === "*") return true
|
|
31
|
+
const target = opaque(etag.trim())
|
|
32
|
+
for (const tag of header.match(ENTITY_TAG_RE) ?? []) {
|
|
33
|
+
if (opaque(tag) === target) return true
|
|
18
34
|
}
|
|
19
|
-
return
|
|
35
|
+
return false
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
export function etag(options?: ETagOptions): MiddlewareFn<{ req: Request }, {}> {
|
|
@@ -30,40 +46,48 @@ export function etag(options?: ETagOptions): MiddlewareFn<{ req: Request }, {}>
|
|
|
30
46
|
|
|
31
47
|
const response = await next()
|
|
32
48
|
|
|
33
|
-
/*
|
|
34
|
-
if (response.status
|
|
49
|
+
/* Only a 200 has a selected representation a 304 can stand in for. */
|
|
50
|
+
if (response.status !== 200) return response
|
|
35
51
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
) {
|
|
41
|
-
return response
|
|
52
|
+
const ifNoneMatch = ctx.req.headers.get("if-none-match")
|
|
53
|
+
|
|
54
|
+
/* A handler-set ETag is authoritative: never overwrite it or hash the body. */
|
|
55
|
+
const existing = response.headers.get("etag")
|
|
56
|
+
if (existing !== null) {
|
|
57
|
+
if (!ifNoneMatchHits(ifNoneMatch, existing)) return response
|
|
58
|
+
await response.body?.cancel().catch(() => {})
|
|
59
|
+
return replaceResponse(response, { body: null, headers: response.headers, status: 304 })
|
|
42
60
|
}
|
|
43
61
|
|
|
44
|
-
/*
|
|
45
|
-
|
|
46
|
-
|
|
62
|
+
/* Never buffer a stream: SSE and generate() can be endless. */
|
|
63
|
+
if (bodyKind(response) !== "buffered") return response
|
|
64
|
+
|
|
65
|
+
let bytes: Uint8Array<ArrayBuffer>
|
|
66
|
+
const raw = rawBodyOf(response)
|
|
67
|
+
if (raw !== null) {
|
|
68
|
+
bytes = typeof raw === "string" ? encoder.encode(raw) : new Uint8Array(raw)
|
|
69
|
+
} else {
|
|
70
|
+
bytes = new Uint8Array(await response.arrayBuffer())
|
|
71
|
+
}
|
|
47
72
|
|
|
48
|
-
|
|
73
|
+
/* Nothing to validate; hand back a fresh body (the original may have been read). */
|
|
74
|
+
if (bytes.byteLength === 0) {
|
|
75
|
+
return raw === null ? replaceResponse(response, { body: bytes, headers: response.headers }) : response
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const hash = await digest(bytes)
|
|
49
79
|
const etagValue = weak ? `W/"${hash}"` : `"${hash}"`
|
|
50
80
|
|
|
51
|
-
const headers = new Headers(response.headers)
|
|
81
|
+
const headers = new Headers(response.headers as HeadersInit)
|
|
52
82
|
headers.set("etag", etagValue)
|
|
53
83
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (ifNoneMatch === "*" || ifNoneMatch?.split(",").some((v) => v.trim() === etagValue)) {
|
|
57
|
-
return replaceResponse(response, {
|
|
58
|
-
body: null,
|
|
59
|
-
headers,
|
|
60
|
-
status: 304,
|
|
61
|
-
})
|
|
84
|
+
if (ifNoneMatchHits(ifNoneMatch, etagValue)) {
|
|
85
|
+
return replaceResponse(response, { body: null, headers, status: 304 })
|
|
62
86
|
}
|
|
63
87
|
|
|
64
|
-
/*
|
|
88
|
+
/* The original body was read (or is raw): always hand back a fresh one. */
|
|
65
89
|
return replaceResponse(response, {
|
|
66
|
-
body,
|
|
90
|
+
body: typeof raw === "string" ? raw : bytes,
|
|
67
91
|
headers,
|
|
68
92
|
status: response.status,
|
|
69
93
|
})
|