@lovrozagar/honey 0.6.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +514 -192
- package/bin/honey.js +25 -0
- package/dist/accepts.d.ts +7 -1
- package/dist/accepts.d.ts.map +1 -1
- package/dist/accepts.js +126 -0
- package/dist/accepts.js.map +1 -0
- package/dist/body-kind.d.ts +26 -0
- package/dist/body-kind.d.ts.map +1 -0
- package/dist/body-kind.js +70 -0
- package/dist/body-kind.js.map +1 -0
- package/dist/body-limit.d.ts +5 -1
- package/dist/body-limit.d.ts.map +1 -1
- package/dist/body-limit.js +126 -0
- package/dist/body-limit.js.map +1 -0
- package/dist/build/index.d.ts +19 -3
- package/dist/build/index.d.ts.map +1 -1
- package/dist/build/index.js +173 -0
- package/dist/build/index.js.map +1 -0
- package/dist/cli-args.d.ts +33 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +102 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-go/auth.go +26 -0
- package/dist/cli-go/config.go +192 -0
- package/dist/cli-go/errors.go +75 -0
- package/dist/cli-go/multipart.go +79 -0
- package/dist/cli-go/output.go +163 -0
- package/dist/cli-go/root.go +30 -0
- package/dist/cli-go/stream.go +88 -0
- package/dist/cli-go/version.go +5 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +209 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/defaults.js +48 -0
- package/dist/client/defaults.js.map +1 -0
- package/dist/client/error.d.ts +35 -0
- package/dist/client/error.d.ts.map +1 -1
- package/dist/client/error.js +71 -0
- package/dist/client/error.js.map +1 -0
- package/dist/client/http.d.ts +67 -8
- package/dist/client/http.d.ts.map +1 -1
- package/dist/client/http.js +571 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +95 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/path.d.ts +26 -0
- package/dist/client/path.d.ts.map +1 -0
- package/dist/client/path.js +163 -0
- package/dist/client/path.js.map +1 -0
- package/dist/client/redirect-policy.d.ts +12 -0
- package/dist/client/redirect-policy.d.ts.map +1 -0
- package/dist/client/redirect-policy.js +20 -0
- package/dist/client/redirect-policy.js.map +1 -0
- package/dist/client/sdk.d.ts +36 -0
- package/dist/client/sdk.d.ts.map +1 -1
- package/dist/client/sdk.js +292 -0
- package/dist/client/sdk.js.map +1 -0
- package/dist/client/sse.d.ts +13 -0
- package/dist/client/sse.d.ts.map +1 -1
- package/dist/client/sse.js +129 -0
- package/dist/client/sse.js.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/ws.d.ts +9 -2
- package/dist/client/ws.d.ts.map +1 -1
- package/dist/client/ws.js +112 -0
- package/dist/client/ws.js.map +1 -0
- package/dist/client-go/errors.go +236 -0
- package/dist/client-go/export.go +44 -0
- package/dist/client-go/invalidation.go +371 -0
- package/dist/client-go/nullable.go +62 -0
- package/dist/client-go/realtime.go +354 -0
- package/dist/client-go/result.go +20 -0
- package/dist/client-go/runtime.go +975 -0
- package/dist/client-go/sse.go +159 -0
- package/dist/client-go/transport.go +495 -0
- package/dist/client-go/ws.go +128 -0
- package/dist/client-info.d.ts +36 -0
- package/dist/client-info.d.ts.map +1 -0
- package/dist/client-info.js +53 -0
- package/dist/client-info.js.map +1 -0
- package/dist/client-mcp/README.md +10 -0
- package/dist/client-mcp/server.ts +221 -0
- package/dist/client-mcp/types.ts +19 -0
- package/dist/client-python/_errors.py +115 -0
- package/dist/client-python/_invalidation.py +494 -0
- package/dist/client-python/_realtime.py +179 -0
- package/dist/client-python/_runtime.py +860 -0
- package/dist/client-python/_sse.py +155 -0
- package/dist/client-python/_transport.py +366 -0
- package/dist/client-python/_ws.py +159 -0
- package/dist/client-python/py.typed +0 -0
- package/dist/client-rust/errors.rs +220 -0
- package/dist/client-rust/invalidation.rs +412 -0
- package/dist/client-rust/invalidation_sync.rs +93 -0
- package/dist/client-rust/nullable.rs +17 -0
- package/dist/client-rust/realtime.rs +702 -0
- package/dist/client-rust/result.rs +22 -0
- package/dist/client-rust/runtime.rs +980 -0
- package/dist/client-rust/runtime_sync.rs +484 -0
- package/dist/client-rust/sse.rs +188 -0
- package/dist/client-rust/ws.rs +167 -0
- package/dist/codegen-go-cli.d.ts.map +1 -1
- package/dist/codegen-go-cli.js +964 -0
- package/dist/codegen-go-cli.js.map +1 -0
- package/dist/codegen-go.d.ts +15 -3
- package/dist/codegen-go.d.ts.map +1 -1
- package/dist/codegen-go.js +872 -0
- package/dist/codegen-go.js.map +1 -0
- package/dist/codegen-hash.js +9 -0
- package/dist/codegen-hash.js.map +1 -0
- package/dist/codegen-ir.d.ts +69 -2
- package/dist/codegen-ir.d.ts.map +1 -1
- package/dist/codegen-ir.js +661 -0
- package/dist/codegen-ir.js.map +1 -0
- package/dist/codegen-lang.d.ts +65 -0
- package/dist/codegen-lang.d.ts.map +1 -0
- package/dist/codegen-lang.js +526 -0
- package/dist/codegen-lang.js.map +1 -0
- package/dist/codegen-load.d.ts +31 -0
- package/dist/codegen-load.d.ts.map +1 -0
- package/dist/codegen-load.js +138 -0
- package/dist/codegen-load.js.map +1 -0
- package/dist/codegen-loaders.js +55 -0
- package/dist/codegen-loaders.js.map +1 -0
- package/dist/codegen-mcp.d.ts.map +1 -1
- package/dist/codegen-mcp.js +368 -0
- package/dist/codegen-mcp.js.map +1 -0
- package/dist/codegen-python.d.ts +13 -1
- package/dist/codegen-python.d.ts.map +1 -1
- package/dist/codegen-python.js +828 -0
- package/dist/codegen-python.js.map +1 -0
- package/dist/codegen-route-tree-intern.d.ts +15 -5
- package/dist/codegen-route-tree-intern.d.ts.map +1 -1
- package/dist/codegen-route-tree-intern.js +221 -0
- package/dist/codegen-route-tree-intern.js.map +1 -0
- package/dist/codegen-rust.d.ts.map +1 -1
- package/dist/codegen-rust.js +1293 -0
- package/dist/codegen-rust.js.map +1 -0
- package/dist/codegen-sanitize.d.ts.map +1 -1
- package/dist/codegen-sanitize.js +56 -0
- package/dist/codegen-sanitize.js.map +1 -0
- package/dist/codegen-schema-naming.d.ts +5 -0
- package/dist/codegen-schema-naming.d.ts.map +1 -1
- package/dist/codegen-schema-naming.js +150 -0
- package/dist/codegen-schema-naming.js.map +1 -0
- package/dist/codegen-sdk-model.d.ts +71 -0
- package/dist/codegen-sdk-model.d.ts.map +1 -0
- package/dist/codegen-sdk-model.js +179 -0
- package/dist/codegen-sdk-model.js.map +1 -0
- package/dist/codegen.d.ts +41 -23
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +4610 -0
- package/dist/codegen.js.map +1 -0
- package/dist/context.d.ts +36 -6
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +242 -0
- package/dist/context.js.map +1 -0
- package/dist/cookie-sign.d.ts +16 -2
- package/dist/cookie-sign.d.ts.map +1 -1
- package/dist/cookie-sign.js +104 -0
- package/dist/cookie-sign.js.map +1 -0
- package/dist/cookie.d.ts +12 -0
- package/dist/cookie.d.ts.map +1 -1
- package/dist/cookie.js +138 -0
- package/dist/cookie.js.map +1 -0
- package/dist/cors.d.ts +1 -0
- package/dist/cors.d.ts.map +1 -1
- package/dist/cors.js +87 -0
- package/dist/cors.js.map +1 -0
- package/dist/crypto.d.ts +6 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +38 -0
- package/dist/crypto.js.map +1 -0
- package/dist/csrf.d.ts +19 -0
- package/dist/csrf.d.ts.map +1 -1
- package/dist/csrf.js +79 -0
- package/dist/csrf.js.map +1 -0
- package/dist/curl-logger.d.ts +14 -3
- package/dist/curl-logger.d.ts.map +1 -1
- package/dist/curl-logger.js +199 -0
- package/dist/curl-logger.js.map +1 -0
- package/dist/detect-runtime.js +13 -0
- package/dist/detect-runtime.js.map +1 -0
- package/dist/dict.d.ts +11 -0
- package/dist/dict.d.ts.map +1 -0
- package/dist/dict.js +13 -0
- package/dist/dict.js.map +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +44 -0
- package/dist/error.js.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/etag.d.ts +2 -0
- package/dist/etag.d.ts.map +1 -1
- package/dist/etag.js +82 -0
- package/dist/etag.js.map +1 -0
- package/dist/feature-detect.d.ts +25 -0
- package/dist/feature-detect.d.ts.map +1 -0
- package/dist/feature-detect.js +144 -0
- package/dist/feature-detect.js.map +1 -0
- package/dist/feature-load.js +36 -0
- package/dist/feature-load.js.map +1 -0
- package/dist/feature-slots.d.ts +44 -0
- package/dist/feature-slots.d.ts.map +1 -0
- package/dist/feature-slots.js +48 -0
- package/dist/feature-slots.js.map +1 -0
- package/dist/fetch-request.d.ts +10 -0
- package/dist/fetch-request.d.ts.map +1 -0
- package/dist/fetch-request.js +13 -0
- package/dist/fetch-request.js.map +1 -0
- package/dist/gen-process.d.ts +15 -0
- package/dist/gen-process.d.ts.map +1 -0
- package/dist/gen-process.js +43 -0
- package/dist/gen-process.js.map +1 -0
- package/dist/gen-write.d.ts +19 -0
- package/dist/gen-write.d.ts.map +1 -0
- package/dist/gen-write.js +120 -0
- package/dist/gen-write.js.map +1 -0
- package/dist/glob.d.ts +21 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +115 -0
- package/dist/glob.js.map +1 -0
- package/dist/go-type-emitter.d.ts +18 -4
- package/dist/go-type-emitter.d.ts.map +1 -1
- package/dist/go-type-emitter.js +594 -0
- package/dist/go-type-emitter.js.map +1 -0
- package/dist/honey-response.d.ts.map +1 -1
- package/dist/honey-response.js +338 -0
- package/dist/honey-response.js.map +1 -0
- package/dist/i18n-slot.d.ts +8 -0
- package/dist/i18n-slot.d.ts.map +1 -1
- package/dist/i18n-slot.js +14 -0
- package/dist/i18n-slot.js.map +1 -0
- package/dist/i18n.d.ts +3 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +250 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +410 -149
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2324 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +140 -0
- package/dist/init.js.map +1 -0
- package/dist/input.js +4 -0
- package/dist/input.js.map +1 -0
- package/dist/invalidate-check.d.ts +11 -0
- package/dist/invalidate-check.d.ts.map +1 -1
- package/dist/invalidate-check.js +163 -0
- package/dist/invalidate-check.js.map +1 -0
- package/dist/invoke-user.d.ts +35 -0
- package/dist/invoke-user.d.ts.map +1 -0
- package/dist/invoke-user.js +73 -0
- package/dist/invoke-user.js.map +1 -0
- package/dist/ip-restrict.d.ts +13 -1
- package/dist/ip-restrict.d.ts.map +1 -1
- package/dist/ip-restrict.js +60 -0
- package/dist/ip-restrict.js.map +1 -0
- package/dist/ip.d.ts +39 -0
- package/dist/ip.d.ts.map +1 -0
- package/dist/ip.js +209 -0
- package/dist/ip.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +113 -0
- package/dist/logger.js.map +1 -0
- package/dist/media-type.d.ts +30 -0
- package/dist/media-type.d.ts.map +1 -0
- package/dist/media-type.js +97 -0
- package/dist/media-type.js.map +1 -0
- package/dist/meta-spec-merge.d.ts +32 -0
- package/dist/meta-spec-merge.d.ts.map +1 -0
- package/dist/meta-spec-merge.js +68 -0
- package/dist/meta-spec-merge.js.map +1 -0
- package/dist/meta-spec.d.ts +16 -3
- package/dist/meta-spec.d.ts.map +1 -1
- package/dist/meta-spec.js +535 -0
- package/dist/meta-spec.js.map +1 -0
- package/dist/middleware.d.ts +23 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +253 -0
- package/dist/middleware.js.map +1 -0
- package/dist/node-request.d.ts +57 -12
- package/dist/node-request.d.ts.map +1 -1
- package/dist/node-request.js +407 -0
- package/dist/node-request.js.map +1 -0
- package/dist/node.d.ts +20 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +396 -0
- package/dist/node.js.map +1 -0
- package/dist/openapi/collect.d.ts +6 -3
- package/dist/openapi/collect.d.ts.map +1 -1
- package/dist/openapi/collect.js +30 -0
- package/dist/openapi/collect.js.map +1 -0
- package/dist/openapi/docs-page.d.ts +42 -0
- package/dist/openapi/docs-page.d.ts.map +1 -0
- package/dist/openapi/docs-page.js +80 -0
- package/dist/openapi/docs-page.js.map +1 -0
- package/dist/openapi/document.d.ts +4 -0
- package/dist/openapi/document.d.ts.map +1 -1
- package/dist/openapi/document.js +785 -0
- package/dist/openapi/document.js.map +1 -0
- package/dist/openapi/json-schema-slot.d.ts +7 -0
- package/dist/openapi/json-schema-slot.d.ts.map +1 -1
- package/dist/openapi/json-schema-slot.js +38 -0
- package/dist/openapi/json-schema-slot.js.map +1 -0
- package/dist/openapi/register.d.ts.map +1 -1
- package/dist/openapi/register.js +20 -0
- package/dist/openapi/register.js.map +1 -0
- package/dist/openapi/scalar.d.ts +3 -1
- package/dist/openapi/scalar.d.ts.map +1 -1
- package/dist/openapi/scalar.js +21 -0
- package/dist/openapi/scalar.js.map +1 -0
- package/dist/openapi/serve.d.ts +9 -0
- package/dist/openapi/serve.d.ts.map +1 -0
- package/dist/openapi/serve.js +36 -0
- package/dist/openapi/serve.js.map +1 -0
- package/dist/openapi/spec-factory.d.ts +30 -1
- package/dist/openapi/spec-factory.d.ts.map +1 -1
- package/dist/openapi/spec-factory.js +52 -0
- package/dist/openapi/spec-factory.js.map +1 -0
- package/dist/openapi/spec.d.ts +14 -5
- package/dist/openapi/spec.d.ts.map +1 -1
- package/dist/openapi/spec.js +50 -0
- package/dist/openapi/spec.js.map +1 -0
- package/dist/openapi/swagger.d.ts +3 -1
- package/dist/openapi/swagger.d.ts.map +1 -1
- package/dist/openapi/swagger.js +22 -0
- package/dist/openapi/swagger.js.map +1 -0
- package/dist/pattern.d.ts +71 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +239 -0
- package/dist/pattern.js.map +1 -0
- package/dist/peer.d.ts +17 -0
- package/dist/peer.d.ts.map +1 -0
- package/dist/peer.js +50 -0
- package/dist/peer.js.map +1 -0
- package/dist/plugin.d.ts +67 -16
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +649 -0
- package/dist/plugin.js.map +1 -0
- package/dist/powered-by.d.ts.map +1 -1
- package/dist/powered-by.js +11 -0
- package/dist/powered-by.js.map +1 -0
- package/dist/pretty-json.d.ts.map +1 -1
- package/dist/pretty-json.js +47 -0
- package/dist/pretty-json.js.map +1 -0
- package/dist/producer-stream.d.ts +47 -0
- package/dist/producer-stream.d.ts.map +1 -0
- package/dist/producer-stream.js +267 -0
- package/dist/producer-stream.js.map +1 -0
- package/dist/proxy.d.ts +47 -8
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +274 -0
- package/dist/proxy.js.map +1 -0
- package/dist/python-type-emitter.d.ts +14 -1
- package/dist/python-type-emitter.d.ts.map +1 -1
- package/dist/python-type-emitter.js +157 -0
- package/dist/python-type-emitter.js.map +1 -0
- package/dist/realtime/bus.d.ts +17 -1
- package/dist/realtime/bus.d.ts.map +1 -1
- package/dist/realtime/bus.js +112 -0
- package/dist/realtime/bus.js.map +1 -0
- package/dist/realtime/register.d.ts +3 -0
- package/dist/realtime/register.d.ts.map +1 -0
- package/dist/realtime/register.js +10 -0
- package/dist/realtime/register.js.map +1 -0
- package/dist/realtime/route.d.ts +65 -26
- package/dist/realtime/route.d.ts.map +1 -1
- package/dist/realtime/route.js +72 -0
- package/dist/realtime/route.js.map +1 -0
- package/dist/realtime/server.d.ts +43 -0
- package/dist/realtime/server.d.ts.map +1 -0
- package/dist/realtime/server.js +255 -0
- package/dist/realtime/server.js.map +1 -0
- package/dist/request-id.d.ts +7 -0
- package/dist/request-id.d.ts.map +1 -1
- package/dist/request-id.js +16 -0
- package/dist/request-id.js.map +1 -0
- package/dist/request-limits.d.ts +3 -0
- package/dist/request-limits.d.ts.map +1 -0
- package/dist/request-limits.js +3 -0
- package/dist/request-limits.js.map +1 -0
- package/dist/request-path.d.ts +33 -0
- package/dist/request-path.d.ts.map +1 -0
- package/dist/request-path.js +178 -0
- package/dist/request-path.js.map +1 -0
- package/dist/request-to-curl.d.ts +7 -1
- package/dist/request-to-curl.d.ts.map +1 -1
- package/dist/request-to-curl.js +48 -0
- package/dist/request-to-curl.js.map +1 -0
- package/dist/response.d.ts +25 -2
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +313 -0
- package/dist/response.js.map +1 -0
- package/dist/rust-type-emitter.d.ts +14 -2
- package/dist/rust-type-emitter.d.ts.map +1 -1
- package/dist/rust-type-emitter.js +463 -0
- package/dist/rust-type-emitter.js.map +1 -0
- package/dist/secure-headers.d.ts.map +1 -1
- package/dist/secure-headers.js +53 -0
- package/dist/secure-headers.js.map +1 -0
- package/dist/serve-register.d.ts +4 -0
- package/dist/serve-register.d.ts.map +1 -1
- package/dist/serve-register.js +9 -0
- package/dist/serve-register.js.map +1 -0
- package/dist/serve-slot.d.ts +3 -0
- package/dist/serve-slot.d.ts.map +1 -1
- package/dist/serve-slot.js +52 -0
- package/dist/serve-slot.js.map +1 -0
- package/dist/serve.d.ts +18 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +157 -0
- package/dist/serve.js.map +1 -0
- package/dist/server-timing.d.ts.map +1 -1
- package/dist/server-timing.js +51 -0
- package/dist/server-timing.js.map +1 -0
- package/dist/static.d.ts +9 -0
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +102 -0
- package/dist/static.js.map +1 -0
- package/dist/telemetry/otel.d.ts +2 -0
- package/dist/telemetry/otel.d.ts.map +1 -1
- package/dist/telemetry/otel.js +135 -0
- package/dist/telemetry/otel.js.map +1 -0
- package/dist/testing.d.ts +12 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +124 -0
- package/dist/testing.js.map +1 -0
- package/dist/timeout.d.ts +6 -0
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +36 -0
- package/dist/timeout.js.map +1 -0
- package/dist/tree.d.ts +133 -36
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +437 -0
- package/dist/tree.js.map +1 -0
- package/dist/trust.d.ts +21 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +100 -0
- package/dist/trust.js.map +1 -0
- package/dist/ts-type-emitter.d.ts.map +1 -1
- package/dist/ts-type-emitter.js +150 -0
- package/dist/ts-type-emitter.js.map +1 -0
- package/dist/type-emitter.d.ts +16 -2
- package/dist/type-emitter.d.ts.map +1 -1
- package/dist/type-emitter.js +729 -0
- package/dist/type-emitter.js.map +1 -0
- package/dist/type-extractor.d.ts +8 -4
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +776 -0
- package/dist/type-extractor.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +96 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +14 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +335 -0
- package/dist/validation.js.map +1 -0
- package/dist/with-headers.d.ts +15 -0
- package/dist/with-headers.d.ts.map +1 -0
- package/dist/with-headers.js +45 -0
- package/dist/with-headers.js.map +1 -0
- package/dist/ws/bun.d.ts +11 -5
- package/dist/ws/bun.d.ts.map +1 -1
- package/dist/ws/bun.js +82 -0
- package/dist/ws/bun.js.map +1 -0
- package/dist/ws/cloudflare.d.ts +25 -3
- package/dist/ws/cloudflare.d.ts.map +1 -1
- package/dist/ws/cloudflare.js +117 -0
- package/dist/ws/cloudflare.js.map +1 -0
- package/dist/ws/deno.d.ts +4 -4
- package/dist/ws/deno.d.ts.map +1 -1
- package/dist/ws/deno.js +133 -0
- package/dist/ws/deno.js.map +1 -0
- package/dist/ws/node.d.ts +20 -3
- package/dist/ws/node.d.ts.map +1 -1
- package/dist/ws/node.js +173 -0
- package/dist/ws/node.js.map +1 -0
- package/dist/ws/session.d.ts +11 -0
- package/dist/ws/session.d.ts.map +1 -0
- package/dist/ws/session.js +60 -0
- package/dist/ws/session.js.map +1 -0
- package/dist/ws/shared.d.ts +65 -0
- package/dist/ws/shared.d.ts.map +1 -0
- package/dist/ws/shared.js +103 -0
- package/dist/ws/shared.js.map +1 -0
- package/dist/ws-origin.d.ts +20 -0
- package/dist/ws-origin.d.ts.map +1 -0
- package/dist/ws-origin.js +41 -0
- package/dist/ws-origin.js.map +1 -0
- package/dist/yaml.d.ts +15 -1
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +101 -0
- package/dist/yaml.js.map +1 -0
- package/docs/invalidate-spec.md +13 -1
- package/docs/meta-spec.md +36 -12
- package/docs/packaging.md +43 -24
- package/docs/regression-matrix/README.md +32 -0
- package/docs/regression-matrix/review.md +44 -0
- package/docs/regression-matrix/ws1-3.md +97 -0
- package/docs/regression-matrix/ws4-8.md +107 -0
- package/docs/regression-matrix/ws9-12.md +266 -0
- package/docs/sdk.md +102 -52
- package/package.json +183 -52
- package/src/accepts.ts +91 -33
- package/src/body-kind.ts +67 -0
- package/src/body-limit.ts +102 -87
- package/src/build/index.ts +104 -54
- package/src/cli-args.ts +123 -0
- package/src/cli-go/auth.go +12 -1
- package/src/cli-go/config.go +29 -2
- package/src/cli-go/errors.go +28 -10
- package/src/cli-go/multipart.go +51 -27
- package/src/cli-go/output.go +58 -4
- package/src/cli-go/root.go +1 -1
- package/src/cli-go/version.go +2 -2
- package/src/cli.ts +154 -148
- package/src/client/error.ts +56 -1
- package/src/client/http.ts +479 -256
- package/src/client/index.ts +55 -17
- package/src/client/path.ts +169 -0
- package/src/client/redirect-policy.ts +19 -0
- package/src/client/sdk.ts +180 -101
- package/src/client/sse.ts +92 -66
- package/src/client/ws.ts +67 -31
- package/src/client-go/errors.go +41 -12
- package/src/client-go/export.go +44 -0
- package/src/client-go/invalidation.go +52 -54
- package/src/client-go/nullable.go +62 -0
- package/src/client-go/realtime.go +147 -110
- package/src/client-go/runtime.go +646 -88
- package/src/client-go/sse.go +104 -94
- package/src/client-go/transport.go +67 -27
- package/src/client-go/ws.go +49 -25
- package/src/client-info.ts +79 -0
- package/src/client-mcp/server.ts +133 -3
- package/src/client-python/_invalidation.py +207 -291
- package/src/client-python/_realtime.py +63 -40
- package/src/client-python/_runtime.py +501 -119
- package/src/client-python/_sse.py +130 -74
- package/src/client-python/_transport.py +37 -9
- package/src/client-python/_ws.py +50 -22
- package/src/client-rust/errors.rs +21 -1
- package/src/client-rust/invalidation.rs +202 -231
- package/src/client-rust/invalidation_sync.rs +16 -229
- package/src/client-rust/nullable.rs +17 -0
- package/src/client-rust/realtime.rs +217 -217
- package/src/client-rust/runtime.rs +602 -177
- package/src/client-rust/runtime_sync.rs +169 -145
- package/src/client-rust/sse.rs +142 -121
- package/src/client-rust/ws.rs +61 -37
- package/src/codegen-go-cli.ts +607 -728
- package/src/codegen-go.ts +710 -770
- package/src/codegen-ir.ts +381 -75
- package/src/codegen-lang.ts +517 -0
- package/src/codegen-load.ts +150 -0
- package/src/codegen-mcp.ts +207 -108
- package/src/codegen-python.ts +684 -816
- package/src/codegen-route-tree-intern.ts +51 -25
- package/src/codegen-rust.ts +946 -1240
- package/src/codegen-sanitize.ts +0 -4
- package/src/codegen-schema-naming.ts +19 -6
- package/src/codegen-sdk-model.ts +228 -0
- package/src/codegen.ts +1488 -1140
- package/src/context.ts +130 -11
- package/src/cookie-sign.ts +85 -16
- package/src/cookie.ts +66 -11
- package/src/cors.ts +55 -54
- package/src/crypto.ts +27 -5
- package/src/csrf.ts +57 -25
- package/src/curl-logger.ts +155 -114
- package/src/dict.ts +12 -0
- package/src/error.ts +4 -2
- package/src/etag.ts +58 -34
- package/src/feature-detect.ts +147 -0
- package/src/feature-slots.ts +70 -0
- package/src/fetch-request.ts +13 -0
- package/src/gen-process.ts +42 -0
- package/src/gen-write.ts +127 -0
- package/src/glob.ts +109 -0
- package/src/go-type-emitter.ts +406 -155
- package/src/honey-response.ts +36 -9
- package/src/i18n-slot.ts +10 -0
- package/src/i18n.ts +98 -23
- package/src/index.ts +1893 -1761
- package/src/init.ts +40 -28
- package/src/invalidate-check.ts +42 -4
- package/src/invoke-user.ts +95 -0
- package/src/ip-restrict.ts +47 -161
- package/src/ip.ts +194 -0
- package/src/logger.ts +65 -36
- package/src/media-type.ts +104 -0
- package/src/meta-spec-merge.ts +80 -0
- package/src/meta-spec.ts +0 -0
- package/src/middleware.ts +112 -12
- package/src/node-request.ts +242 -52
- package/src/node.ts +318 -75
- package/src/openapi/collect.ts +19 -44
- package/src/openapi/docs-page.ts +89 -0
- package/src/openapi/document.ts +503 -224
- package/src/openapi/json-schema-slot.ts +40 -0
- package/src/openapi/register.ts +7 -2
- package/src/openapi/scalar.ts +11 -10
- package/src/openapi/serve.ts +39 -0
- package/src/openapi/spec-factory.ts +50 -1
- package/src/openapi/spec.ts +53 -30
- package/src/openapi/swagger.ts +12 -11
- package/src/pattern.ts +245 -0
- package/src/peer.ts +53 -0
- package/src/plugin.ts +270 -185
- package/src/powered-by.ts +2 -2
- package/src/pretty-json.ts +25 -9
- package/src/producer-stream.ts +296 -0
- package/src/proxy.ts +271 -55
- package/src/python-type-emitter.ts +68 -56
- package/src/realtime/bus.ts +56 -9
- package/src/realtime/register.ts +13 -0
- package/src/realtime/route.ts +129 -80
- package/src/realtime/server.ts +298 -0
- package/src/request-id.ts +13 -3
- package/src/request-limits.ts +2 -0
- package/src/request-path.ts +165 -0
- package/src/request-to-curl.ts +28 -7
- package/src/response.ts +143 -103
- package/src/rust-type-emitter.ts +265 -170
- package/src/secure-headers.ts +8 -4
- package/src/serve-register.ts +4 -0
- package/src/serve-slot.ts +38 -0
- package/src/serve.ts +113 -25
- package/src/server-timing.ts +11 -7
- package/src/static.ts +85 -32
- package/src/telemetry/otel.ts +29 -7
- package/src/testing.ts +76 -14
- package/src/timeout.ts +15 -7
- package/src/tree.ts +405 -333
- package/src/trust.ts +117 -0
- package/src/ts-type-emitter.ts +18 -12
- package/src/type-emitter.ts +227 -89
- package/src/type-extractor.ts +562 -424
- package/src/types.ts +8 -0
- package/src/validation.ts +221 -96
- package/src/with-headers.ts +45 -0
- package/src/ws/bun.ts +53 -15
- package/src/ws/cloudflare.ts +81 -19
- package/src/ws/deno.ts +124 -36
- package/src/ws/node.ts +173 -79
- package/src/ws/session.ts +73 -0
- package/src/ws/shared.ts +140 -0
- package/src/ws-origin.ts +53 -0
- package/src/yaml.ts +45 -22
- package/dist/client-mcp/server.d.ts +0 -10
- package/dist/client-mcp/server.d.ts.map +0 -1
- package/dist/client-mcp/types.d.ts +0 -16
- package/dist/client-mcp/types.d.ts.map +0 -1
- package/dist/cloudflare/realtime.d.ts +0 -30
- package/dist/cloudflare/realtime.d.ts.map +0 -1
- package/dist/realtime/buffer.d.ts +0 -18
- package/dist/realtime/buffer.d.ts.map +0 -1
- package/dist/realtime/longpoll.d.ts +0 -23
- package/dist/realtime/longpoll.d.ts.map +0 -1
- package/dist/realtime/protocol.d.ts +0 -50
- package/dist/realtime/protocol.d.ts.map +0 -1
- package/dist/realtime/runtime.d.ts +0 -74
- package/dist/realtime/runtime.d.ts.map +0 -1
- package/src/cloudflare/realtime.ts +0 -234
- package/src/realtime/buffer.ts +0 -101
- package/src/realtime/longpoll.ts +0 -148
- package/src/realtime/protocol.ts +0 -128
- package/src/realtime/runtime.ts +0 -427
package/src/context.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { isNodeOutbound } from "./honey-response.ts"
|
|
2
|
+
import type { HoneyError } from "./error.ts"
|
|
3
|
+
import type { ProducerOptions } from "./producer-stream.ts"
|
|
2
4
|
import type { SSEOptions, SSEStream, TypedResponse } from "./response.ts"
|
|
3
5
|
import { HoneyRes } from "./response.ts"
|
|
6
|
+
import { dict } from "./dict.ts"
|
|
7
|
+
import { peerAddressOf } from "./peer.ts"
|
|
8
|
+
import { resolveClient, TRUST_OFF, type ClientInfo, type TrustSetting } from "./client-info.ts"
|
|
4
9
|
import type { PendingTap } from "./types.ts"
|
|
5
10
|
import { EMPTY_OBJ } from "./types.ts"
|
|
6
11
|
import { parseCookies } from "./validation.ts"
|
|
@@ -11,11 +16,22 @@ import { parseCookies } from "./validation.ts"
|
|
|
11
16
|
*/
|
|
12
17
|
class ContextRes extends HoneyRes {
|
|
13
18
|
private _req: Request
|
|
19
|
+
private _ctx: HoneyContext<unknown>
|
|
14
20
|
private _lastEventId: string | undefined | false = false
|
|
15
21
|
|
|
16
|
-
constructor(req: Request) {
|
|
22
|
+
constructor(req: Request, ctx: HoneyContext<unknown>) {
|
|
17
23
|
super(isNodeOutbound(req))
|
|
18
24
|
this._req = req
|
|
25
|
+
this._ctx = ctx
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Streams end with the request: `ctx.signal`. Producer failures go to the app logger. */
|
|
29
|
+
protected override _producer(): ProducerOptions {
|
|
30
|
+
const ctx = this._ctx
|
|
31
|
+
return {
|
|
32
|
+
report: (err) => reportStreamError(ctx, err),
|
|
33
|
+
signal: () => ctx.signal,
|
|
34
|
+
}
|
|
19
35
|
}
|
|
20
36
|
|
|
21
37
|
override sse(
|
|
@@ -33,23 +49,41 @@ class ContextRes extends HoneyRes {
|
|
|
33
49
|
}
|
|
34
50
|
function parseSearch(ctx: HoneyContext): void {
|
|
35
51
|
const url = ctx._lzUrlFn ? ctx._lzUrlFn() : new URL(ctx.req.url)
|
|
36
|
-
|
|
37
|
-
const
|
|
52
|
+
/* keys are request data: no prototype, so `?__proto__=` or `?toString=` is just a key */
|
|
53
|
+
const first = dict<string>()
|
|
54
|
+
const all = dict<string[]>()
|
|
38
55
|
for (const [key, value] of url.searchParams) {
|
|
39
|
-
|
|
56
|
+
const values = all[key]
|
|
57
|
+
if (values === undefined) {
|
|
40
58
|
first[key] = value
|
|
41
59
|
all[key] = [value]
|
|
42
60
|
} else {
|
|
43
|
-
|
|
44
|
-
if (values) {
|
|
45
|
-
values.push(value)
|
|
46
|
-
}
|
|
61
|
+
values.push(value)
|
|
47
62
|
}
|
|
48
63
|
}
|
|
49
64
|
ctx._lzSearch = first
|
|
50
65
|
ctx._lzSearchAll = all
|
|
51
66
|
}
|
|
52
67
|
|
|
68
|
+
/** What `ctx._rq` carries for client-info resolution; the dispatcher's FetchCtx has these fields. */
|
|
69
|
+
type ClientSource = { env?: unknown; request?: Request; trust?: TrustSetting }
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The client address, scheme and host of the request `ctx` serves, decided by the app's
|
|
73
|
+
* `trustProxy()` setting. `ctx.ip` is its `ip`.
|
|
74
|
+
*/
|
|
75
|
+
export function clientInfo(ctx: HoneyContext<never> | HoneyContext): ClientInfo {
|
|
76
|
+
const c = ctx as HoneyContext
|
|
77
|
+
if (c._lzClient === null) {
|
|
78
|
+
const src = (c._rq ?? {}) as ClientSource
|
|
79
|
+
/* the peer is registered on the request the adapter handed over; headers are read from
|
|
80
|
+
* ctx.req, which survives Deno's upgrade (a header snapshot) */
|
|
81
|
+
const peer = peerAddressOf(src.request ?? c.req, src.env ?? c.env)
|
|
82
|
+
c._lzClient = resolveClient(src.trust ?? TRUST_OFF, c.req, peer)
|
|
83
|
+
}
|
|
84
|
+
return c._lzClient
|
|
85
|
+
}
|
|
86
|
+
|
|
53
87
|
function bgSwallow(p: Promise<unknown>) {
|
|
54
88
|
p.catch(noop)
|
|
55
89
|
}
|
|
@@ -63,6 +97,44 @@ function noop() {}
|
|
|
63
97
|
* `declare` fields have no private brand, so Omit<HoneyContext, "res">
|
|
64
98
|
* works structurally for ApplyOutput type narrowing.
|
|
65
99
|
*/
|
|
100
|
+
type Logger = { error?: (obj: unknown, msg?: string) => void }
|
|
101
|
+
|
|
102
|
+
function reportStreamError(ctx: HoneyContext<unknown>, err: unknown): void {
|
|
103
|
+
const log = (ctx._rq as { log?: Logger } | null)?.log
|
|
104
|
+
if (typeof log?.error === "function") {
|
|
105
|
+
log.error({ err, path: ctx.path }, "stream producer failed")
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
console.error("honey: stream producer failed", err)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Abort `ctx.signal` (with `reason`) for the request `ctx` serves: `timeout()` uses it. Streams
|
|
113
|
+
* still running for the request end, and handlers that pass the signal on stop their work.
|
|
114
|
+
*/
|
|
115
|
+
export function abortRequest(ctx: HoneyContext<never> | HoneyContext, reason?: unknown): void {
|
|
116
|
+
const c = ctx as HoneyContext
|
|
117
|
+
if (c._ac === null) linkSignal(c)
|
|
118
|
+
c._ac?.abort(reason)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Create `ctx.signal`, following the request's own signal (client disconnect, shutdown). */
|
|
122
|
+
function linkSignal(ctx: HoneyContext): AbortSignal {
|
|
123
|
+
const ac = new AbortController()
|
|
124
|
+
ctx._ac = ac
|
|
125
|
+
let request: AbortSignal | undefined
|
|
126
|
+
try {
|
|
127
|
+
request = ctx.req.signal
|
|
128
|
+
} catch {
|
|
129
|
+
/* a request shim without a signal: only timeout() can abort */
|
|
130
|
+
}
|
|
131
|
+
if (request !== undefined && request !== null) {
|
|
132
|
+
if (request.aborted) ac.abort(request.reason)
|
|
133
|
+
else request.addEventListener("abort", () => ac.abort(request.reason), { once: true })
|
|
134
|
+
}
|
|
135
|
+
return ac.signal
|
|
136
|
+
}
|
|
137
|
+
|
|
66
138
|
export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
67
139
|
readonly background: (p: Promise<unknown>) => void
|
|
68
140
|
readonly env: TEnv
|
|
@@ -75,14 +147,39 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
75
147
|
declare readonly errors: Record<string, (...args: never[]) => unknown>
|
|
76
148
|
declare readonly executionCtx: { waitUntil?: (p: Promise<unknown>) => void } | undefined
|
|
77
149
|
declare readonly headers: Record<string, string>
|
|
150
|
+
/**
|
|
151
|
+
* The client's IP address in canonical form (`::ffff:1.2.3.4` is `1.2.3.4`), or `null` when
|
|
152
|
+
* the runtime does not report one. Behind reverse proxies, set `app.trustProxy()`; until then
|
|
153
|
+
* this is the proxy's address and forwarding headers are ignored.
|
|
154
|
+
*/
|
|
155
|
+
declare readonly ip: string | null
|
|
78
156
|
declare readonly meta: Record<string, unknown>
|
|
79
157
|
declare readonly path: string
|
|
80
|
-
|
|
158
|
+
/** Publish into a realtime namespace (see `RealtimePublisher`); spelled out so generated types inline it. */
|
|
159
|
+
declare readonly realtime: {
|
|
160
|
+
publish(topic: string, data: unknown): void
|
|
161
|
+
namespace(name: string): { publish(topic: string, data: unknown): void }
|
|
162
|
+
}
|
|
81
163
|
declare readonly routePattern: string
|
|
82
164
|
declare readonly search: Record<string, string>
|
|
83
165
|
declare readonly searchAll: Record<string, string[]>
|
|
166
|
+
/**
|
|
167
|
+
* Aborts when the client disconnects, when `timeout()` fires, or when the server shuts down.
|
|
168
|
+
* Pass it to `fetch()`, database drivers and timers so abandoned work stops; streams from
|
|
169
|
+
* `ctx.res.sse()`, `stream()` and `generate()` already end with it.
|
|
170
|
+
*/
|
|
171
|
+
declare readonly signal: AbortSignal
|
|
172
|
+
/**
|
|
173
|
+
* The error behind the response, once a handler or a deeper middleware threw and the
|
|
174
|
+
* `next()` boundary turned it into an error response; `undefined` while nothing failed.
|
|
175
|
+
* Read it after `await next()` for metrics, logs or reporting: `errorKey`, `status`, and
|
|
176
|
+
* `cause` (the original throw when it was not a `HoneyError`). Later errors replace it.
|
|
177
|
+
*/
|
|
178
|
+
declare readonly error: HoneyError | undefined
|
|
84
179
|
|
|
85
180
|
/* backing state for lazy getters — stored directly on instance to avoid extra object allocation */
|
|
181
|
+
/** @internal */ _ac: AbortController | null
|
|
182
|
+
/** @internal */ _lzClient: ClientInfo | null
|
|
86
183
|
/** @internal */ _lzCookies: Record<string, string> | null
|
|
87
184
|
/** @internal */ _lzHeaders: Record<string, string> | null
|
|
88
185
|
/** @internal */ _lzSearch: Record<string, string> | null
|
|
@@ -96,6 +193,9 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
96
193
|
/* tap side-effects — queued by c.tap(), drained after handler */
|
|
97
194
|
/** @internal */ _pendingTaps: PendingTap[] | null
|
|
98
195
|
|
|
196
|
+
/* the request this context serves — read by the error boundary and terminal handlers */
|
|
197
|
+
/** @internal */ _rq: unknown
|
|
198
|
+
|
|
99
199
|
constructor(opts: {
|
|
100
200
|
env: TEnv
|
|
101
201
|
executionCtx?: { waitUntil?: (p: Promise<unknown>) => void }
|
|
@@ -109,7 +209,7 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
109
209
|
this.req = opts.req
|
|
110
210
|
this.env = opts.env
|
|
111
211
|
this.params = opts.params
|
|
112
|
-
this.res = new ContextRes(opts.req)
|
|
212
|
+
this.res = new ContextRes(opts.req, this as HoneyContext<unknown>)
|
|
113
213
|
|
|
114
214
|
/* direct assignment — no defineProperty */
|
|
115
215
|
;(this as Record<string, unknown>)["executionCtx"] = opts.executionCtx
|
|
@@ -127,6 +227,8 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
127
227
|
: bgSwallow
|
|
128
228
|
|
|
129
229
|
/* lazy getter backing — flat on instance, no extra object */
|
|
230
|
+
this._ac = null
|
|
231
|
+
this._lzClient = null
|
|
130
232
|
this._lzCookies = null
|
|
131
233
|
this._lzHeaders = null
|
|
132
234
|
this._lzSearch = null
|
|
@@ -134,11 +236,14 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
134
236
|
this._lzUrlFn = opts.urlFn ?? null
|
|
135
237
|
|
|
136
238
|
/* error propagation */
|
|
239
|
+
;(this as Record<string, unknown>)["error"] = undefined
|
|
137
240
|
this._errorToResponse = null
|
|
138
241
|
this._isErrorResponse = false
|
|
139
242
|
|
|
140
243
|
/* taps */
|
|
141
244
|
this._pendingTaps = null
|
|
245
|
+
|
|
246
|
+
this._rq = null
|
|
142
247
|
}
|
|
143
248
|
|
|
144
249
|
/** @internal */
|
|
@@ -173,7 +278,7 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
173
278
|
enumerable: true,
|
|
174
279
|
get(this: HoneyContext): Record<string, string> {
|
|
175
280
|
if (this._lzHeaders === null) {
|
|
176
|
-
const h
|
|
281
|
+
const h = dict<string>()
|
|
177
282
|
this.req.headers.forEach((value, key) => {
|
|
178
283
|
h[key] = value
|
|
179
284
|
})
|
|
@@ -182,6 +287,20 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
|
|
|
182
287
|
return this._lzHeaders
|
|
183
288
|
},
|
|
184
289
|
})
|
|
290
|
+
Object.defineProperty(HoneyContext.prototype, "ip", {
|
|
291
|
+
configurable: true,
|
|
292
|
+
enumerable: true,
|
|
293
|
+
get(this: HoneyContext): string | null {
|
|
294
|
+
return clientInfo(this).ip
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
Object.defineProperty(HoneyContext.prototype, "signal", {
|
|
298
|
+
configurable: true,
|
|
299
|
+
enumerable: true,
|
|
300
|
+
get(this: HoneyContext): AbortSignal {
|
|
301
|
+
return this._ac === null ? linkSignal(this) : this._ac.signal
|
|
302
|
+
},
|
|
303
|
+
})
|
|
185
304
|
Object.defineProperty(HoneyContext.prototype, "search", {
|
|
186
305
|
configurable: true,
|
|
187
306
|
enumerable: true,
|
package/src/cookie-sign.ts
CHANGED
|
@@ -1,10 +1,56 @@
|
|
|
1
1
|
const encoder = new TextEncoder()
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Signed value formats:
|
|
5
|
+
*
|
|
6
|
+
* - v2 (current): `<value>.~<sig>`, where `sig` is the HMAC-SHA256 of
|
|
7
|
+
* `"honey-cookie-v2\0" + name + "\0" + value`. The cookie name is bound into
|
|
8
|
+
* the MAC, so a signature cannot be replayed under another cookie that shares
|
|
9
|
+
* the secret. `~` is outside the base64url alphabet, so the two formats never
|
|
10
|
+
* overlap.
|
|
11
|
+
* - v1 (legacy): `<value>.<sig>`, the HMAC of the value alone. `verify` accepts
|
|
12
|
+
* it while `legacy` is true (the default) so cookies issued before the change
|
|
13
|
+
* keep working during a migration window.
|
|
14
|
+
*/
|
|
15
|
+
const V2_MARKER = "~"
|
|
16
|
+
const V2_DOMAIN = "honey-cookie-v2\0"
|
|
17
|
+
/* An HMAC-SHA256 tag is 32 bytes: 43 base64url characters, no padding. */
|
|
18
|
+
const SIG_RE = /^[A-Za-z0-9_-]{43}$/
|
|
19
|
+
|
|
20
|
+
export type SignOptions = {
|
|
21
|
+
/** The cookie name to bind the signature to. Pass the same name to `verify`. */
|
|
22
|
+
name?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type VerifyOptions = {
|
|
26
|
+
/** The cookie name the signature must be bound to. */
|
|
27
|
+
name?: string
|
|
28
|
+
/**
|
|
29
|
+
* Also accept legacy (v1) signatures, which are not bound to a name.
|
|
30
|
+
* Default `true`; set `false` once every v1 cookie has expired.
|
|
31
|
+
*/
|
|
32
|
+
legacy?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const MAX_CACHED_KEYS = 32
|
|
36
|
+
const keyCache = new Map<string, Promise<CryptoKey>>()
|
|
37
|
+
|
|
3
38
|
function importKey(secret: string): Promise<CryptoKey> {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
|
|
39
|
+
let key = keyCache.get(secret)
|
|
40
|
+
if (key === undefined) {
|
|
41
|
+
key = crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, [
|
|
42
|
+
"sign",
|
|
43
|
+
"verify",
|
|
44
|
+
])
|
|
45
|
+
if (keyCache.size >= MAX_CACHED_KEYS) {
|
|
46
|
+
const oldest = keyCache.keys().next().value
|
|
47
|
+
if (oldest !== undefined) keyCache.delete(oldest)
|
|
48
|
+
}
|
|
49
|
+
keyCache.set(secret, key)
|
|
50
|
+
/* A rejected import must not stay cached. */
|
|
51
|
+
key.catch(() => keyCache.delete(secret))
|
|
52
|
+
}
|
|
53
|
+
return key
|
|
8
54
|
}
|
|
9
55
|
|
|
10
56
|
function toBase64Url(buffer: ArrayBuffer): string {
|
|
@@ -16,34 +62,57 @@ function toBase64Url(buffer: ArrayBuffer): string {
|
|
|
16
62
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
|
|
17
63
|
}
|
|
18
64
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
65
|
+
/** Strict decode: the input must be the canonical encoding of a 32-byte tag. */
|
|
66
|
+
function fromBase64Url(str: string): Uint8Array<ArrayBuffer> | null {
|
|
67
|
+
if (!SIG_RE.test(str)) return null
|
|
68
|
+
let binary: string
|
|
69
|
+
try {
|
|
70
|
+
binary = atob(str.replace(/-/g, "+").replace(/_/g, "/"))
|
|
71
|
+
} catch {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
22
74
|
const bytes = new Uint8Array(binary.length)
|
|
23
75
|
for (let i = 0; i < binary.length; i++) {
|
|
24
76
|
bytes[i] = binary.charCodeAt(i)
|
|
25
77
|
}
|
|
78
|
+
/* Reject non-zero trailing bits, which would make signatures malleable. */
|
|
79
|
+
if (toBase64Url(bytes.buffer) !== str) return null
|
|
26
80
|
return bytes
|
|
27
81
|
}
|
|
28
82
|
|
|
29
|
-
|
|
83
|
+
function v2Payload(name: string, value: string): Uint8Array<ArrayBuffer> {
|
|
84
|
+
return encoder.encode(`${V2_DOMAIN}${name}\0${value}`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function sign(value: string, secret: string, opts: SignOptions = {}): Promise<string> {
|
|
30
88
|
const key = await importKey(secret)
|
|
31
|
-
const signature = await crypto.subtle.sign("HMAC", key,
|
|
32
|
-
return `${value}.${toBase64Url(signature)}`
|
|
89
|
+
const signature = await crypto.subtle.sign("HMAC", key, v2Payload(opts.name ?? "", value))
|
|
90
|
+
return `${value}.${V2_MARKER}${toBase64Url(signature)}`
|
|
33
91
|
}
|
|
34
92
|
|
|
35
|
-
|
|
36
|
-
|
|
93
|
+
/** Returns the original value, or `null` when no secret verifies it or the input is malformed. */
|
|
94
|
+
export async function verify(signed: string, secrets: string[], opts: VerifyOptions = {}): Promise<string | null> {
|
|
95
|
+
if (typeof signed !== "string" || signed.length === 0) return null
|
|
37
96
|
const lastDot = signed.lastIndexOf(".")
|
|
38
97
|
if (lastDot === -1) return null
|
|
39
98
|
|
|
40
99
|
const value = signed.slice(0, lastDot)
|
|
41
|
-
|
|
100
|
+
let sigText = signed.slice(lastDot + 1)
|
|
101
|
+
const isV2 = sigText.startsWith(V2_MARKER)
|
|
102
|
+
if (isV2) sigText = sigText.slice(V2_MARKER.length)
|
|
103
|
+
else if (opts.legacy === false) return null
|
|
104
|
+
|
|
105
|
+
const sig = fromBase64Url(sigText)
|
|
106
|
+
if (sig === null) return null
|
|
107
|
+
const payload = isV2 ? v2Payload(opts.name ?? "", value) : encoder.encode(value)
|
|
42
108
|
|
|
43
109
|
for (const secret of secrets) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
110
|
+
try {
|
|
111
|
+
const key = await importKey(secret)
|
|
112
|
+
if (await crypto.subtle.verify("HMAC", key, sig, payload)) return value
|
|
113
|
+
} catch {
|
|
114
|
+
/* An unusable secret never verifies. */
|
|
115
|
+
}
|
|
47
116
|
}
|
|
48
117
|
|
|
49
118
|
return null
|
package/src/cookie.ts
CHANGED
|
@@ -9,8 +9,11 @@ export type CookieOptions = {
|
|
|
9
9
|
value: string
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"])
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
|
-
* Percent-encode characters outside the RFC 6265 cookie-octet range
|
|
15
|
+
* Percent-encode characters outside the RFC 6265 cookie-octet range, plus `%`
|
|
16
|
+
* itself so the value always decodes back to what was set.
|
|
14
17
|
* Cookie-octets: 0x21, 0x23-0x2B, 0x2D-0x3A, 0x3C-0x5B, 0x5D-0x7E
|
|
15
18
|
*/
|
|
16
19
|
function encodeCookieValue(value: string): string {
|
|
@@ -18,13 +21,16 @@ function encodeCookieValue(value: string): string {
|
|
|
18
21
|
for (const ch of value) {
|
|
19
22
|
const c = ch.codePointAt(0) ?? 0
|
|
20
23
|
if (
|
|
21
|
-
c
|
|
22
|
-
(c
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
c !== 0x25 &&
|
|
25
|
+
(c === 0x21 ||
|
|
26
|
+
(c >= 0x23 && c <= 0x2b) ||
|
|
27
|
+
(c >= 0x2d && c <= 0x3a) ||
|
|
28
|
+
(c >= 0x3c && c <= 0x5b) ||
|
|
29
|
+
(c >= 0x5d && c <= 0x7e))
|
|
26
30
|
) {
|
|
27
31
|
encoded += ch
|
|
32
|
+
} else if (c >= 0xd800 && c <= 0xdfff) {
|
|
33
|
+
throw new TypeError("Cookie value contains a lone surrogate and cannot be encoded")
|
|
28
34
|
} else {
|
|
29
35
|
encoded += encodeURIComponent(ch)
|
|
30
36
|
}
|
|
@@ -32,7 +38,52 @@ function encodeCookieValue(value: string): string {
|
|
|
32
38
|
return encoded
|
|
33
39
|
}
|
|
34
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Decode `%XX` sequences that form valid UTF-8 and keep every other byte as
|
|
43
|
+
* sent. A stray `%` in a foreign cookie no longer disables decoding for the
|
|
44
|
+
* whole value.
|
|
45
|
+
*/
|
|
46
|
+
export function decodeCookieValue(value: string): string {
|
|
47
|
+
if (value.indexOf("%") === -1) return value
|
|
48
|
+
try {
|
|
49
|
+
return decodeURIComponent(value)
|
|
50
|
+
} catch {
|
|
51
|
+
return value.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {
|
|
52
|
+
try {
|
|
53
|
+
return decodeURIComponent(run)
|
|
54
|
+
} catch {
|
|
55
|
+
return run
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse a `Cookie` request header. When a name repeats, the first value wins:
|
|
63
|
+
* browsers send the cookie with the most specific path first (RFC 6265 §5.4).
|
|
64
|
+
* Names are trimmed on both sides; empty names and prototype keys are skipped.
|
|
65
|
+
*/
|
|
66
|
+
export function parseCookieHeader(header: string): Record<string, string> {
|
|
67
|
+
const result: Record<string, string> = {}
|
|
68
|
+
if (header.length === 0) return result
|
|
69
|
+
for (const pair of header.split(";")) {
|
|
70
|
+
const eqIdx = pair.indexOf("=")
|
|
71
|
+
if (eqIdx === -1) continue
|
|
72
|
+
const name = pair.slice(0, eqIdx).trim()
|
|
73
|
+
if (name.length === 0 || UNSAFE_KEYS.has(name) || Object.hasOwn(result, name)) continue
|
|
74
|
+
let value = pair.slice(eqIdx + 1).trim()
|
|
75
|
+
/* RFC 6265: strip surrounding double quotes before decoding, so an encoded quote survives. */
|
|
76
|
+
if (value.length >= 2 && value.charCodeAt(0) === 34 && value.charCodeAt(value.length - 1) === 34) {
|
|
77
|
+
value = value.slice(1, -1)
|
|
78
|
+
}
|
|
79
|
+
result[name] = decodeCookieValue(value)
|
|
80
|
+
}
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
83
|
+
|
|
35
84
|
const validCookieNameRe = /^[\w!#$%&'*.^`|~+-]+$/
|
|
85
|
+
/* A host name or IP literal, optionally with a leading dot. */
|
|
86
|
+
const validDomainRe = /^\.?(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]+$|^\[[0-9A-Fa-f:.]+\]$/
|
|
36
87
|
|
|
37
88
|
export function serializeCookie(name: string, opts: CookieOptions): string {
|
|
38
89
|
if (!validCookieNameRe.test(name)) {
|
|
@@ -42,7 +93,9 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
|
|
|
42
93
|
let secure = opts.secure
|
|
43
94
|
let path = opts.path
|
|
44
95
|
|
|
45
|
-
|
|
96
|
+
/* Browsers match the prefixes case-insensitively (RFC 6265bis §4.1.3). */
|
|
97
|
+
const lower = name.toLowerCase()
|
|
98
|
+
if (lower.startsWith("__host-")) {
|
|
46
99
|
if (!opts.secure) throw new Error("__Host- cookies require secure: true")
|
|
47
100
|
if (opts.domain) throw new Error("__Host- cookies must not set domain")
|
|
48
101
|
if (opts.path !== "/" && opts.path !== undefined) {
|
|
@@ -50,7 +103,7 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
|
|
|
50
103
|
}
|
|
51
104
|
secure = true
|
|
52
105
|
path = path ?? "/"
|
|
53
|
-
} else if (
|
|
106
|
+
} else if (lower.startsWith("__secure-")) {
|
|
54
107
|
if (!opts.secure) throw new Error("__Secure- cookies require secure: true")
|
|
55
108
|
secure = true
|
|
56
109
|
}
|
|
@@ -59,10 +112,11 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
|
|
|
59
112
|
throw new Error("SameSite=None cookies require secure: true")
|
|
60
113
|
}
|
|
61
114
|
|
|
62
|
-
if (opts.domain &&
|
|
115
|
+
if (opts.domain && !validDomainRe.test(opts.domain)) {
|
|
63
116
|
throw new Error("Cookie domain contains invalid characters")
|
|
64
117
|
}
|
|
65
|
-
|
|
118
|
+
/* av-value: any CHAR except CTLs or ";" (RFC 6265 §4.1.1). */
|
|
119
|
+
if (path && /[^\x20-\x3a\x3c-\x7e]/.test(path)) {
|
|
66
120
|
throw new Error("Cookie path contains invalid characters")
|
|
67
121
|
}
|
|
68
122
|
|
|
@@ -73,7 +127,8 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
|
|
|
73
127
|
if (!Number.isFinite(opts.maxAge) || opts.maxAge < 0) {
|
|
74
128
|
throw new Error(`Invalid cookie Max-Age: ${opts.maxAge}`)
|
|
75
129
|
}
|
|
76
|
-
|
|
130
|
+
/* Clamp so the value prints as digits; browsers cap Max-Age at 400 days anyway. */
|
|
131
|
+
cookie += `; Max-Age=${Math.min(Math.floor(opts.maxAge), Number.MAX_SAFE_INTEGER)}`
|
|
77
132
|
}
|
|
78
133
|
if (opts.expires) {
|
|
79
134
|
if (Number.isNaN(opts.expires.getTime())) {
|
package/src/cors.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { namedMiddleware } from "./middleware.ts"
|
|
2
|
-
import { replaceResponse } from "./honey-response.ts"
|
|
3
2
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
3
|
+
import { withHeaders } from "./with-headers.ts"
|
|
4
4
|
|
|
5
5
|
export type CORSOptions = {
|
|
6
|
+
/** Requires an explicit `origin` list, string or predicate; `"*"` or no origin throws. */
|
|
6
7
|
credentials?: boolean
|
|
7
8
|
exposeHeaders?: string[]
|
|
8
9
|
headers?: string[]
|
|
@@ -14,79 +15,79 @@ export type CORSOptions = {
|
|
|
14
15
|
const DEFAULT_METHODS = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"]
|
|
15
16
|
const DEFAULT_MAX_AGE = 86400
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Returns the value for `Access-Control-Allow-Origin`, or null when the origin
|
|
20
|
+
* is not allowed. The opaque origin `null` (sandboxed iframes, `file:`, some
|
|
21
|
+
* redirects) is never reflected: any page can produce it.
|
|
22
|
+
*/
|
|
17
23
|
function matchOrigin(origin: string, config: CORSOptions["origin"]): string | null {
|
|
18
24
|
if (config === undefined || config === "*") return "*"
|
|
25
|
+
if (origin === "null") return null
|
|
19
26
|
if (typeof config === "string") return config === origin ? origin : null
|
|
20
27
|
if (Array.isArray(config)) return config.includes(origin) ? origin : null
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
return config(origin) ? origin : null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Append `value` to `Vary` unless it is already listed (or `*`). */
|
|
32
|
+
function addVary(headers: Headers, value: string): void {
|
|
33
|
+
const current = headers.get("vary")
|
|
34
|
+
if (current === null || current.trim() === "") {
|
|
35
|
+
headers.set("vary", value)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const tokens = current.split(",").map((t) => t.trim().toLowerCase())
|
|
39
|
+
if (tokens.includes("*") || tokens.includes(value.toLowerCase())) return
|
|
40
|
+
headers.set("vary", `${current}, ${value}`)
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
export function cors(options?: CORSOptions): MiddlewareFn<{ req: Request }, {}> {
|
|
26
44
|
const opts = options ?? {}
|
|
27
45
|
|
|
28
|
-
|
|
29
|
-
|
|
46
|
+
if (opts.credentials === true && (opts.origin === undefined || opts.origin === "*")) {
|
|
47
|
+
throw new TypeError(
|
|
48
|
+
"cors: `credentials: true` needs an explicit `origin` (string, list or predicate). A wildcard would let every site make credentialed requests and read the response.",
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* `Vary: Origin` whenever the answer depends on the request's Origin — that is,
|
|
53
|
+
* on every response unless the policy is a plain wildcard. Without it a shared
|
|
54
|
+
* cache can serve one origin's ACAO (or its absence) to another. */
|
|
55
|
+
const varies = !(opts.origin === undefined || opts.origin === "*")
|
|
56
|
+
const methods = (opts.methods ?? DEFAULT_METHODS).join(", ")
|
|
57
|
+
const allowHeaders = opts.headers && opts.headers.length > 0 ? opts.headers.join(", ") : null
|
|
58
|
+
const exposeHeaders = opts.exposeHeaders && opts.exposeHeaders.length > 0 ? opts.exposeHeaders.join(", ") : null
|
|
59
|
+
const maxAge = String(opts.maxAge ?? DEFAULT_MAX_AGE)
|
|
30
60
|
|
|
31
61
|
const mw: MiddlewareFn<{ req: Request }, {}> = async (ctx, next) => {
|
|
32
62
|
const req = ctx.req
|
|
33
63
|
const requestOrigin = req.headers.get("origin")
|
|
64
|
+
const allowedOrigin = requestOrigin === null ? null : matchOrigin(requestOrigin, opts.origin)
|
|
34
65
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/* origin not allowed */
|
|
43
|
-
if (allowedOrigin === null) {
|
|
44
|
-
return next()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const isPreflight = req.method === "OPTIONS" && req.headers.has("access-control-request-method")
|
|
48
|
-
|
|
49
|
-
/* Vary: Origin whenever response depends on the request origin (non-wildcard) */
|
|
50
|
-
const needsVary = allowedOrigin !== "*"
|
|
51
|
-
|
|
52
|
-
if (isPreflight) {
|
|
53
|
-
const corsHeaders = new Headers()
|
|
54
|
-
corsHeaders.set("access-control-allow-origin", allowedOrigin)
|
|
55
|
-
corsHeaders.set("access-control-allow-methods", (opts.methods ?? DEFAULT_METHODS).join(", "))
|
|
56
|
-
if (opts.headers && opts.headers.length > 0) {
|
|
57
|
-
corsHeaders.set("access-control-allow-headers", opts.headers.join(", "))
|
|
66
|
+
if (allowedOrigin !== null && req.method === "OPTIONS" && req.headers.has("access-control-request-method")) {
|
|
67
|
+
const headers = new Headers()
|
|
68
|
+
headers.set("access-control-allow-origin", allowedOrigin)
|
|
69
|
+
headers.set("access-control-allow-methods", methods)
|
|
70
|
+
if (allowHeaders !== null) {
|
|
71
|
+
headers.set("access-control-allow-headers", allowHeaders)
|
|
58
72
|
} else {
|
|
59
73
|
const requested = req.headers.get("access-control-request-headers")
|
|
60
|
-
if (requested)
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
corsHeaders.set("access-control-max-age", String(opts.maxAge ?? DEFAULT_MAX_AGE))
|
|
65
|
-
if (opts.credentials) {
|
|
66
|
-
corsHeaders.set("access-control-allow-credentials", "true")
|
|
74
|
+
if (requested) headers.set("access-control-allow-headers", requested)
|
|
75
|
+
addVary(headers, "Access-Control-Request-Headers")
|
|
67
76
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return new Response(null, { headers
|
|
77
|
+
headers.set("access-control-max-age", maxAge)
|
|
78
|
+
if (opts.credentials) headers.set("access-control-allow-credentials", "true")
|
|
79
|
+
if (varies) addVary(headers, "Origin")
|
|
80
|
+
return new Response(null, { headers, status: 204 })
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
/* simple/actual request */
|
|
75
83
|
const response = await next()
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
headers.set("access-control-expose-headers",
|
|
83
|
-
}
|
|
84
|
-
if (needsVary) {
|
|
85
|
-
headers.append("vary", "Origin")
|
|
86
|
-
}
|
|
87
|
-
return replaceResponse(response, {
|
|
88
|
-
headers,
|
|
89
|
-
status: response.status,
|
|
84
|
+
if (allowedOrigin === null && !varies) return response
|
|
85
|
+
return withHeaders(response, (headers) => {
|
|
86
|
+
if (varies) addVary(headers, "Origin")
|
|
87
|
+
if (allowedOrigin === null) return
|
|
88
|
+
headers.set("access-control-allow-origin", allowedOrigin)
|
|
89
|
+
if (opts.credentials) headers.set("access-control-allow-credentials", "true")
|
|
90
|
+
if (exposeHeaders !== null) headers.set("access-control-expose-headers", exposeHeaders)
|
|
90
91
|
})
|
|
91
92
|
}
|
|
92
93
|
|