@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/init.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
2
|
import { basename, dirname, join } from "node:path"
|
|
3
3
|
import { fileURLToPath } from "node:url"
|
|
4
|
+
import type { InitFlags } from "./cli-args.ts"
|
|
4
5
|
|
|
5
|
-
export type InitFlags
|
|
6
|
-
|
|
7
|
-
force: boolean
|
|
8
|
-
}
|
|
6
|
+
export type { InitFlags } from "./cli-args.ts"
|
|
7
|
+
export { parseInitFlags } from "./cli-args.ts"
|
|
9
8
|
|
|
10
9
|
type PackageJson = {
|
|
11
10
|
dependencies?: Record<string, string>
|
|
@@ -50,13 +49,6 @@ export default {
|
|
|
50
49
|
}
|
|
51
50
|
`
|
|
52
51
|
|
|
53
|
-
export function parseInitFlags(args: string[]): InitFlags {
|
|
54
|
-
return {
|
|
55
|
-
cf: args.includes("--cf"),
|
|
56
|
-
force: args.includes("--force"),
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
52
|
export function runInit(cwd: string, flags: InitFlags): void {
|
|
61
53
|
const files: Record<string, string> = {
|
|
62
54
|
"src/app.ts": APP_TS,
|
|
@@ -76,7 +68,7 @@ export function runInit(cwd: string, flags: InitFlags): void {
|
|
|
76
68
|
for (const [rel, contents] of Object.entries(files)) {
|
|
77
69
|
writeText(join(cwd, rel), contents)
|
|
78
70
|
}
|
|
79
|
-
writePackageJson(cwd)
|
|
71
|
+
writePackageJson(cwd, flags.force)
|
|
80
72
|
console.log("honey: initialized")
|
|
81
73
|
}
|
|
82
74
|
|
|
@@ -91,25 +83,45 @@ function wranglerJsonc(name: string): string {
|
|
|
91
83
|
`
|
|
92
84
|
}
|
|
93
85
|
|
|
94
|
-
|
|
86
|
+
const PACKAGE_NAME = "@lovrozagar/honey"
|
|
87
|
+
|
|
88
|
+
const SCRIPTS: Record<string, string> = {
|
|
89
|
+
dev: "bun --watch src/server.ts",
|
|
90
|
+
generate: "honey generate",
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates package.json, or adds what is missing to an existing one. Existing scripts, `type` and
|
|
95
|
+
* dependency ranges are kept unless `force` is set; each kept value is reported.
|
|
96
|
+
*/
|
|
97
|
+
function writePackageJson(cwd: string, force: boolean): void {
|
|
95
98
|
const path = join(cwd, "package.json")
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
scripts
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
const exists = existsSync(path)
|
|
100
|
+
const existing = exists ? readPackageJson(path) : {}
|
|
101
|
+
const notes: string[] = []
|
|
102
|
+
|
|
103
|
+
const scripts = { ...existing.scripts }
|
|
104
|
+
for (const [name, command] of Object.entries(SCRIPTS)) {
|
|
105
|
+
const current = scripts[name]
|
|
106
|
+
if (current === undefined || current === command || force) scripts[name] = command
|
|
107
|
+
else notes.push(`kept scripts.${name} ("${current}"); honey expects "${command}"`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const dependencies = { ...existing.dependencies }
|
|
111
|
+
if (dependencies[PACKAGE_NAME] === undefined || force) dependencies[PACKAGE_NAME] = `^${honeyVersion()}`
|
|
112
|
+
|
|
113
|
+
const next: PackageJson = { ...existing, dependencies, scripts }
|
|
114
|
+
if (!exists) {
|
|
115
|
+
next.name = packageNameFromDir(cwd)
|
|
116
|
+
next.private = true
|
|
117
|
+
}
|
|
118
|
+
if (existing.type === undefined) {
|
|
119
|
+
if (!exists || force) next.type = "module"
|
|
120
|
+
else notes.push('package.json has no "type"; the scaffold is ESM, so set "type": "module" or rerun with --force')
|
|
111
121
|
}
|
|
122
|
+
|
|
112
123
|
writeText(path, `${JSON.stringify(next, null, "\t")}\n`)
|
|
124
|
+
for (const note of notes) console.log(`honey: ${note}`)
|
|
113
125
|
}
|
|
114
126
|
|
|
115
127
|
function readPackageJson(path: string): PackageJson {
|
package/src/invalidate-check.ts
CHANGED
|
@@ -62,8 +62,15 @@ function declaredNothing(meta: Record<string, unknown> | null): boolean {
|
|
|
62
62
|
return value === null || (Array.isArray(value) && value.length === 0)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Did the author declare targets? Read from the meta, not the emitted operation: a profile
|
|
67
|
+
* that leaves `x-invalidate` out of its document must not make every declared mutation look
|
|
68
|
+
* undeclared.
|
|
69
|
+
*/
|
|
70
|
+
function alreadyInvalidates(entry: InvalidateCheckOperation): boolean {
|
|
71
|
+
const declared = entry.meta?.invalidate
|
|
72
|
+
if (Array.isArray(declared) && declared.length > 0) return true
|
|
73
|
+
const emitted = entry.operation["x-invalidate"]
|
|
67
74
|
return Array.isArray(emitted) && emitted.length > 0
|
|
68
75
|
}
|
|
69
76
|
|
|
@@ -112,7 +119,7 @@ export function findMissingInvalidate(
|
|
|
112
119
|
const findings: InvalidateFinding[] = []
|
|
113
120
|
for (const entry of operations) {
|
|
114
121
|
if (!MUTATION_METHODS.has(entry.method)) continue
|
|
115
|
-
if (alreadyInvalidates(entry
|
|
122
|
+
if (alreadyInvalidates(entry)) continue
|
|
116
123
|
if (declaredNothing(entry.meta)) continue
|
|
117
124
|
|
|
118
125
|
let affects: string[] = []
|
|
@@ -120,7 +127,9 @@ export function findMissingInvalidate(
|
|
|
120
127
|
if (entity !== undefined && entity !== null) {
|
|
121
128
|
/* sharper than path shape: the same entity is read somewhere else in the document */
|
|
122
129
|
affects = readsByEntity.get(JSON.stringify(entity)) ?? []
|
|
123
|
-
}
|
|
130
|
+
}
|
|
131
|
+
if (affects.length === 0) {
|
|
132
|
+
/* no reader is tagged with the entity — path shape is still a signal, not silence */
|
|
124
133
|
affects = pathFamily(entry.path)
|
|
125
134
|
.filter((candidate) => readPaths.has(candidate))
|
|
126
135
|
.map((candidate) => `GET ${candidate}`)
|
|
@@ -165,3 +174,32 @@ export function reportMissingInvalidate(
|
|
|
165
174
|
if (level === "error") throw new Error(report)
|
|
166
175
|
console.warn(report)
|
|
167
176
|
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* `invalidate` selectors that name no route. A selector is `"<METHOD> <pattern>"`, exactly as
|
|
180
|
+
* the generated `RouteSelector` union spells it; anything else invalidates nothing at runtime.
|
|
181
|
+
*/
|
|
182
|
+
export function findInvalidSelectors(
|
|
183
|
+
operations: readonly InvalidateCheckOperation[],
|
|
184
|
+
inventory: ReadonlySet<string>,
|
|
185
|
+
): Array<{ method: string; path: string; selector: unknown }> {
|
|
186
|
+
const out: Array<{ method: string; path: string; selector: unknown }> = []
|
|
187
|
+
const seen = new Set<string>()
|
|
188
|
+
for (const entry of operations) {
|
|
189
|
+
const declared = entry.meta?.invalidate
|
|
190
|
+
if (!Array.isArray(declared)) continue
|
|
191
|
+
for (const selector of declared) {
|
|
192
|
+
if (typeof selector === "string" && inventory.has(selector)) continue
|
|
193
|
+
const key = `${entry.method} ${entry.path} ${String(selector)}`
|
|
194
|
+
if (seen.has(key)) continue
|
|
195
|
+
seen.add(key)
|
|
196
|
+
out.push({ method: entry.method.toUpperCase(), path: entry.path, selector })
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return out
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** The level an `invalidate` config reports at. */
|
|
203
|
+
export function invalidateLevel(config: InvalidateCheckConfig | undefined): InvalidateCheckLevel {
|
|
204
|
+
return resolveConfig(config).level
|
|
205
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Containment for user callbacks the framework calls outside a request's middleware chain
|
|
3
|
+
* (WebSocket and realtime events, adapter hooks). A sync throw or a rejection never escapes
|
|
4
|
+
* into the runtime, where it would be an uncaught exception or an unhandled rejection and,
|
|
5
|
+
* on Node, end the process.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** The error side of a structured logger (`error(obj, msg)`), e.g. honey/logger. */
|
|
9
|
+
export type InvokeLogger = {
|
|
10
|
+
error?: (obj: Record<string, unknown>, msg?: string) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type InvokeOptions = {
|
|
14
|
+
/** Where the callback's error goes. When it throws or rejects itself, that is logged and it is not called again for its own error. */
|
|
15
|
+
onError?: ((err: unknown) => unknown) | null
|
|
16
|
+
/** The app logger; `console.error` when absent. */
|
|
17
|
+
log?: InvokeLogger | null
|
|
18
|
+
/** A label for the log line (`"open"`, `"message"`, …). */
|
|
19
|
+
phase: string
|
|
20
|
+
/** Extra fields for the log line. */
|
|
21
|
+
fields?: Record<string, unknown>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function logFailure(err: unknown, opts: InvokeOptions, phase: string): void {
|
|
25
|
+
const fields = { ...opts.fields, err, phase }
|
|
26
|
+
try {
|
|
27
|
+
if (opts.log?.error) {
|
|
28
|
+
opts.log.error(fields, "callback failed")
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
/* a broken logger must not take the callback's place as the thing that escapes */
|
|
33
|
+
}
|
|
34
|
+
console.error("honey: callback failed", fields)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isThenable(value: unknown): value is PromiseLike<unknown> {
|
|
38
|
+
return value !== null && typeof value === "object" && typeof (value as { then?: unknown }).then === "function"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Report `err` through `onError`, or log it. Never throws, never rejects. */
|
|
42
|
+
export function reportUserError(err: unknown, opts: InvokeOptions): void {
|
|
43
|
+
const onError = opts.onError
|
|
44
|
+
if (!onError) {
|
|
45
|
+
logFailure(err, opts, opts.phase)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const r = onError(err)
|
|
50
|
+
if (isThenable(r)) r.then(undefined, (e: unknown) => logFailure(e, opts, "onError"))
|
|
51
|
+
} catch (e) {
|
|
52
|
+
logFailure(e, opts, "onError")
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Call `fn` and resolve once it settles. Resolves `true` when it succeeded and `false` when it
|
|
58
|
+
* threw or rejected (the error went to `onError` or the logger). Never rejects.
|
|
59
|
+
*/
|
|
60
|
+
export function invokeUser(fn: () => unknown, opts: InvokeOptions): Promise<boolean> {
|
|
61
|
+
try {
|
|
62
|
+
const r = fn()
|
|
63
|
+
if (isThenable(r)) {
|
|
64
|
+
return Promise.resolve(r).then(
|
|
65
|
+
() => true,
|
|
66
|
+
(e: unknown) => {
|
|
67
|
+
reportUserError(e, opts)
|
|
68
|
+
return false
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
return Promise.resolve(true)
|
|
73
|
+
} catch (e) {
|
|
74
|
+
reportUserError(e, opts)
|
|
75
|
+
return Promise.resolve(false)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* One ordered event queue: each task starts after the previous one settled, and a failing task
|
|
81
|
+
* never stops the ones after it.
|
|
82
|
+
*/
|
|
83
|
+
export function createEventQueue(): { push(task: () => Promise<unknown>): Promise<void> } {
|
|
84
|
+
let tail: Promise<void> = Promise.resolve()
|
|
85
|
+
return {
|
|
86
|
+
push(task) {
|
|
87
|
+
const next = tail.then(task).then(
|
|
88
|
+
() => undefined,
|
|
89
|
+
() => undefined,
|
|
90
|
+
)
|
|
91
|
+
tail = next
|
|
92
|
+
return next
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/ip-restrict.ts
CHANGED
|
@@ -1,153 +1,29 @@
|
|
|
1
1
|
import { namedMiddleware } from "./middleware.ts"
|
|
2
2
|
import { HoneyError } from "./error.ts"
|
|
3
|
+
import { ipInRange, parseIp, parseIpRange, type IpRange } from "./ip.ts"
|
|
3
4
|
import type { MiddlewareFn } from "./middleware.ts"
|
|
4
5
|
import { EK, SK } from "./types.ts"
|
|
5
6
|
|
|
6
7
|
type IpRestrictOptions = {
|
|
8
|
+
/** Addresses and CIDR ranges allowed through. When set, everything else is rejected. */
|
|
7
9
|
allowList?: string[]
|
|
10
|
+
/** Addresses and CIDR ranges rejected. Checked before `allowList`. */
|
|
8
11
|
denyList?: string[]
|
|
12
|
+
/**
|
|
13
|
+
* Returns the client address, replacing `ctx.ip`. Leave it out to use `ctx.ip`, which the
|
|
14
|
+
* app's `trustProxy()` setting decides — the same address every other feature sees.
|
|
15
|
+
*/
|
|
9
16
|
getIp?: (req: Request) => string | null
|
|
10
|
-
trustProxy?: boolean
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
function
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const cfIp = req.headers.get("cf-connecting-ip")
|
|
22
|
-
if (cfIp) return stripBrackets(cfIp.trim())
|
|
23
|
-
|
|
24
|
-
return null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function proxyAwareGetIp(req: Request): string | null {
|
|
28
|
-
const cfIp = req.headers.get("cf-connecting-ip")
|
|
29
|
-
if (cfIp) return stripBrackets(cfIp.trim())
|
|
30
|
-
|
|
31
|
-
const xff = req.headers.get("x-forwarded-for")
|
|
32
|
-
if (xff) return stripBrackets(xff.split(",")[0].trim())
|
|
33
|
-
|
|
34
|
-
const realIp = req.headers.get("x-real-ip")
|
|
35
|
-
if (realIp) return stripBrackets(realIp.trim())
|
|
36
|
-
|
|
37
|
-
return null
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function parseIpv4(ip: string): number | null {
|
|
41
|
-
const parts = ip.split(".")
|
|
42
|
-
if (parts.length !== 4) return null
|
|
43
|
-
let result = 0
|
|
44
|
-
for (const part of parts) {
|
|
45
|
-
const n = Number.parseInt(part, 10)
|
|
46
|
-
if (Number.isNaN(n) || n < 0 || n > 255) return null
|
|
47
|
-
result = (result << 8) | n
|
|
48
|
-
}
|
|
49
|
-
return result >>> 0
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
type Ipv4CidrRule = {
|
|
53
|
-
base: number
|
|
54
|
-
mask: number
|
|
55
|
-
v6: false
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
type Ipv6CidrRule = {
|
|
59
|
-
base: bigint
|
|
60
|
-
mask: bigint
|
|
61
|
-
v6: true
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
type CidrRule = Ipv4CidrRule | Ipv6CidrRule
|
|
65
|
-
|
|
66
|
-
type ParsedRule = {
|
|
67
|
-
cidr?: CidrRule
|
|
68
|
-
exact: string
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function expandIpv6(ip: string): bigint | null {
|
|
72
|
-
let addr = ip
|
|
73
|
-
|
|
74
|
-
/* handle ::ffff:1.2.3.4 mapped IPv4 */
|
|
75
|
-
const v4Suffix = addr.match(/:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/)
|
|
76
|
-
if (v4Suffix) {
|
|
77
|
-
const v4 = parseIpv4(v4Suffix[1])
|
|
78
|
-
if (v4 === null) return null
|
|
79
|
-
addr = `${addr.slice(0, addr.lastIndexOf(":"))}:${((v4 >>> 16) & 0xffff).toString(16)}:${(v4 & 0xffff).toString(16)}`
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* expand :: */
|
|
83
|
-
const halves = addr.split("::")
|
|
84
|
-
if (halves.length > 2) return null
|
|
85
|
-
|
|
86
|
-
let groups: string[]
|
|
87
|
-
if (halves.length === 2) {
|
|
88
|
-
const left = halves[0] === "" ? [] : halves[0].split(":")
|
|
89
|
-
const right = halves[1] === "" ? [] : halves[1].split(":")
|
|
90
|
-
const fill = 8 - left.length - right.length
|
|
91
|
-
if (fill < 0) return null
|
|
92
|
-
groups = [...left, ...Array.from<string>({ length: fill }).fill("0"), ...right]
|
|
93
|
-
} else {
|
|
94
|
-
groups = addr.split(":")
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (groups.length !== 8) return null
|
|
98
|
-
|
|
99
|
-
let result = 0n
|
|
100
|
-
for (const g of groups) {
|
|
101
|
-
const n = Number.parseInt(g, 16)
|
|
102
|
-
if (Number.isNaN(n) || n < 0 || n > 0xffff) return null
|
|
103
|
-
result = (result << 16n) | BigInt(n)
|
|
104
|
-
}
|
|
105
|
-
return result
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function isIpv6(ip: string): boolean {
|
|
109
|
-
return ip.includes(":")
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function parseRule(rule: string): ParsedRule {
|
|
113
|
-
const slashIdx = rule.indexOf("/")
|
|
114
|
-
if (slashIdx === -1) {
|
|
115
|
-
return { exact: rule }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const ip = rule.slice(0, slashIdx)
|
|
119
|
-
const bits = Number.parseInt(rule.slice(slashIdx + 1), 10)
|
|
120
|
-
|
|
121
|
-
if (isIpv6(ip)) {
|
|
122
|
-
const base = expandIpv6(ip)
|
|
123
|
-
if (base === null || Number.isNaN(bits) || bits < 0 || bits > 128) {
|
|
124
|
-
return { exact: rule }
|
|
125
|
-
}
|
|
126
|
-
const mask = bits === 0 ? 0n : ((1n << BigInt(bits)) - 1n) << BigInt(128 - bits)
|
|
127
|
-
return { cidr: { base: base & mask, mask, v6: true }, exact: rule }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const base = parseIpv4(ip)
|
|
131
|
-
if (base === null || Number.isNaN(bits) || bits < 0 || bits > 32) {
|
|
132
|
-
return { exact: rule }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const mask = bits === 0 ? 0 : (~0 << (32 - bits)) >>> 0
|
|
136
|
-
return { cidr: { base: (base & mask) >>> 0, mask, v6: false }, exact: rule }
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function matchesRule(ip: string, rule: ParsedRule): boolean {
|
|
140
|
-
if (rule.cidr) {
|
|
141
|
-
if (rule.cidr.v6) {
|
|
142
|
-
const parsed = expandIpv6(ip)
|
|
143
|
-
if (parsed === null) return false
|
|
144
|
-
return (parsed & rule.cidr.mask) === rule.cidr.base
|
|
145
|
-
}
|
|
146
|
-
const parsed = parseIpv4(ip)
|
|
147
|
-
if (parsed === null) return false
|
|
148
|
-
return (parsed & rule.cidr.mask) >>> 0 === rule.cidr.base
|
|
149
|
-
}
|
|
150
|
-
return ip === rule.exact
|
|
19
|
+
function parseRules(list: readonly string[] | undefined, name: string): IpRange[] {
|
|
20
|
+
if (list === undefined) return []
|
|
21
|
+
return list.map((rule) => {
|
|
22
|
+
const range = typeof rule === "string" ? parseIpRange(rule) : null
|
|
23
|
+
if (range === null)
|
|
24
|
+
throw new Error(`ipRestrict: ${name} entry ${JSON.stringify(rule)} is not an IP address or CIDR range`)
|
|
25
|
+
return range
|
|
26
|
+
})
|
|
151
27
|
}
|
|
152
28
|
|
|
153
29
|
function throwForbidden(): never {
|
|
@@ -157,35 +33,45 @@ function throwForbidden(): never {
|
|
|
157
33
|
})
|
|
158
34
|
}
|
|
159
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Allows or rejects requests by client address. Rules and addresses are compared in canonical
|
|
38
|
+
* numeric form, so `::ffff:203.0.113.7`, `203.0.113.7` and `203.0.113.7:51234` are one address.
|
|
39
|
+
*
|
|
40
|
+
* Fails closed: a request whose address is unknown or unparseable is rejected with 403, under
|
|
41
|
+
* a deny list as much as under an allow list.
|
|
42
|
+
*/
|
|
160
43
|
export function ipRestrict(opts: IpRestrictOptions): MiddlewareFn<{ req: Request }, {}> {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
44
|
+
const legacy = opts as { trustCloudflare?: unknown; trustProxy?: unknown }
|
|
45
|
+
if (legacy.trustProxy !== undefined || legacy.trustCloudflare !== undefined) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"ipRestrict: `trustProxy` and `trustCloudflare` moved to the app. Call `app.trustProxy(1)` " +
|
|
48
|
+
"(one reverse proxy) or `app.trustProxy([...proxy ranges])`; on Cloudflare Workers `ctx.ip` " +
|
|
49
|
+
"already reads CF-Connecting-IP.",
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
if (opts.allowList !== undefined && opts.allowList.length === 0) {
|
|
53
|
+
throw new Error("ipRestrict: `allowList: []` would reject every request. Remove the middleware or list addresses.")
|
|
54
|
+
}
|
|
55
|
+
const denyRules = parseRules(opts.denyList, "denyList")
|
|
56
|
+
const allowRules = parseRules(opts.allowList, "allowList")
|
|
57
|
+
if (denyRules.length === 0 && allowRules.length === 0) {
|
|
58
|
+
throw new Error("ipRestrict: pass a non-empty `allowList` or `denyList`")
|
|
59
|
+
}
|
|
60
|
+
const getIp = opts.getIp
|
|
165
61
|
|
|
166
62
|
const mw: MiddlewareFn<{ req: Request }, {}> = (ctx, next) => {
|
|
167
|
-
const
|
|
63
|
+
const raw = getIp ? getIp(ctx.req) : ((ctx as { ip?: string | null }).ip ?? null)
|
|
64
|
+
const addr = raw === null || raw === "" ? null : parseIp(raw)
|
|
168
65
|
|
|
169
|
-
/*
|
|
170
|
-
if (
|
|
171
|
-
for (const rule of denyRules) {
|
|
172
|
-
if (matchesRule(ip, rule)) throwForbidden()
|
|
173
|
-
}
|
|
174
|
-
}
|
|
66
|
+
/* fail closed: an unknown address can match neither list */
|
|
67
|
+
if (addr === null) throwForbidden()
|
|
175
68
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (ip === null) throwForbidden()
|
|
179
|
-
let allowed = false
|
|
180
|
-
for (const rule of allowRules) {
|
|
181
|
-
if (matchesRule(ip, rule)) {
|
|
182
|
-
allowed = true
|
|
183
|
-
break
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (!allowed) throwForbidden()
|
|
69
|
+
for (const rule of denyRules) {
|
|
70
|
+
if (ipInRange(addr, rule)) throwForbidden()
|
|
187
71
|
}
|
|
188
72
|
|
|
73
|
+
if (allowRules.length > 0 && !allowRules.some((rule) => ipInRange(addr, rule))) throwForbidden()
|
|
74
|
+
|
|
189
75
|
return next()
|
|
190
76
|
}
|
|
191
77
|
|
package/src/ip.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP addresses in canonical numeric form. Every comparison the framework makes (`ipRestrict`
|
|
3
|
+
* rules, trusted proxy ranges) parses both sides here first, so `::ffff:203.0.113.7`,
|
|
4
|
+
* `203.0.113.7` and `203.0.113.7:51234` are one address and `2001:DB8::1` equals `2001:db8::1`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type IpAddress = { readonly v: 4; readonly n: number } | { readonly v: 6; readonly n: bigint }
|
|
8
|
+
|
|
9
|
+
export type IpRange =
|
|
10
|
+
| { readonly v: 4; readonly base: number; readonly mask: number }
|
|
11
|
+
| { readonly v: 6; readonly base: bigint; readonly mask: bigint }
|
|
12
|
+
|
|
13
|
+
const V4_MAPPED_PREFIX = 0xffffn << 32n
|
|
14
|
+
const V4_MAPPED_MASK = ~0xffffffffn & ((1n << 128n) - 1n)
|
|
15
|
+
|
|
16
|
+
function parseIpv4(s: string): number | null {
|
|
17
|
+
const parts = s.split(".")
|
|
18
|
+
if (parts.length !== 4) return null
|
|
19
|
+
let n = 0
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
/* digits only, no leading zeros (`010` is octal to some parsers, decimal to others) */
|
|
22
|
+
if (part.length === 0 || part.length > 3 || !/^\d+$/.test(part)) return null
|
|
23
|
+
if (part.length > 1 && part.charCodeAt(0) === 48) return null
|
|
24
|
+
const octet = Number(part)
|
|
25
|
+
if (octet > 255) return null
|
|
26
|
+
n = n * 256 + octet
|
|
27
|
+
}
|
|
28
|
+
return n
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The `:`-separated hex groups of one side of `::`; `null` when a group is not 1–4 hex digits. */
|
|
32
|
+
function groupsOf(part: string): string[] | null {
|
|
33
|
+
if (part === "") return []
|
|
34
|
+
const groups = part.split(":")
|
|
35
|
+
for (const g of groups) {
|
|
36
|
+
if (!/^[0-9A-Fa-f]{1,4}$/.test(g)) return null
|
|
37
|
+
}
|
|
38
|
+
return groups
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseIpv6(s: string): bigint | null {
|
|
42
|
+
let addr = s
|
|
43
|
+
let tail: number[] = []
|
|
44
|
+
const lastColon = addr.lastIndexOf(":")
|
|
45
|
+
if (lastColon !== -1 && addr.indexOf(".", lastColon) !== -1) {
|
|
46
|
+
const v4 = parseIpv4(addr.slice(lastColon + 1))
|
|
47
|
+
if (v4 === null) return null
|
|
48
|
+
tail = [Math.floor(v4 / 65536), v4 % 65536]
|
|
49
|
+
addr = addr.slice(0, lastColon + 1)
|
|
50
|
+
/* "::1.2.3.4" leaves "::", "a::1.2.3.4" leaves "a::"; "a:1.2.3.4" leaves "a:" */
|
|
51
|
+
if (addr.endsWith(":") && !addr.endsWith("::")) addr = addr.slice(0, -1)
|
|
52
|
+
}
|
|
53
|
+
const halves = addr.split("::")
|
|
54
|
+
if (halves.length > 2) return null
|
|
55
|
+
const left = groupsOf(halves[0])
|
|
56
|
+
if (left === null) return null
|
|
57
|
+
let groups: number[]
|
|
58
|
+
if (halves.length === 2) {
|
|
59
|
+
const right = groupsOf(halves[1])
|
|
60
|
+
if (right === null) return null
|
|
61
|
+
const fill = 8 - left.length - right.length - tail.length
|
|
62
|
+
if (fill < 1) return null
|
|
63
|
+
groups = [...left.map(hex), ...Array.from({ length: fill }, () => 0), ...right.map(hex), ...tail]
|
|
64
|
+
} else {
|
|
65
|
+
groups = [...left.map(hex), ...tail]
|
|
66
|
+
}
|
|
67
|
+
if (groups.length !== 8) return null
|
|
68
|
+
let n = 0n
|
|
69
|
+
for (const g of groups) n = (n << 16n) | BigInt(g)
|
|
70
|
+
return n
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function hex(g: string): number {
|
|
74
|
+
return Number.parseInt(g, 16)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** An IPv4-mapped IPv6 address (`::ffff:a.b.c.d`) is the IPv4 address it maps. */
|
|
78
|
+
function fromV6(n: bigint): IpAddress {
|
|
79
|
+
if ((n & V4_MAPPED_MASK) === V4_MAPPED_PREFIX) return { n: Number(n & 0xffffffffn), v: 4 }
|
|
80
|
+
return { n, v: 6 }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parses an address as a peer or a forwarding header writes it: brackets, an IPv6 zone
|
|
85
|
+
* (`%eth0`) and a port (`1.2.3.4:5678`, `[::1]:5678`) are accepted and dropped. Returns
|
|
86
|
+
* `null` for anything that is not exactly one address.
|
|
87
|
+
*/
|
|
88
|
+
export function parseIp(input: string): IpAddress | null {
|
|
89
|
+
let s = input.trim()
|
|
90
|
+
if (s.startsWith("[")) {
|
|
91
|
+
const close = s.indexOf("]")
|
|
92
|
+
if (close === -1) return null
|
|
93
|
+
const after = s.slice(close + 1)
|
|
94
|
+
if (after !== "" && !/^:\d{1,5}$/.test(after)) return null
|
|
95
|
+
s = s.slice(1, close)
|
|
96
|
+
} else if (s.indexOf(":") !== -1 && s.indexOf(":") === s.lastIndexOf(":") && s.includes(".")) {
|
|
97
|
+
/* "1.2.3.4:5678" — one colon and a dot can only be IPv4 with a port */
|
|
98
|
+
const [host, port] = s.split(":")
|
|
99
|
+
if (!/^\d{1,5}$/.test(port)) return null
|
|
100
|
+
s = host
|
|
101
|
+
}
|
|
102
|
+
const zone = s.indexOf("%")
|
|
103
|
+
if (zone !== -1) {
|
|
104
|
+
if (!s.includes(":")) return null
|
|
105
|
+
s = s.slice(0, zone)
|
|
106
|
+
}
|
|
107
|
+
if (s.includes(":")) {
|
|
108
|
+
const n = parseIpv6(s)
|
|
109
|
+
return n === null ? null : fromV6(n)
|
|
110
|
+
}
|
|
111
|
+
const n = parseIpv4(s)
|
|
112
|
+
return n === null ? null : { n, v: 4 }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Canonical text: dotted IPv4, or RFC 5952 IPv6 (lowercase, longest zero run compressed). */
|
|
116
|
+
export function formatIp(addr: IpAddress): string {
|
|
117
|
+
if (addr.v === 4) {
|
|
118
|
+
const n = addr.n
|
|
119
|
+
return `${Math.floor(n / 16777216)}.${Math.floor(n / 65536) % 256}.${Math.floor(n / 256) % 256}.${n % 256}`
|
|
120
|
+
}
|
|
121
|
+
const groups: number[] = []
|
|
122
|
+
for (let i = 7; i >= 0; i--) groups.push(Number((addr.n >> BigInt(i * 16)) & 0xffffn))
|
|
123
|
+
let bestStart = -1
|
|
124
|
+
let bestLen = 1
|
|
125
|
+
for (let i = 0; i < 8;) {
|
|
126
|
+
if (groups[i] !== 0) {
|
|
127
|
+
i++
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
let j = i
|
|
131
|
+
while (j < 8 && groups[j] === 0) j++
|
|
132
|
+
if (j - i > bestLen) {
|
|
133
|
+
bestStart = i
|
|
134
|
+
bestLen = j - i
|
|
135
|
+
}
|
|
136
|
+
i = j
|
|
137
|
+
}
|
|
138
|
+
const text = groups.map((g) => g.toString(16))
|
|
139
|
+
if (bestStart === -1) return text.join(":")
|
|
140
|
+
const head = text.slice(0, bestStart).join(":")
|
|
141
|
+
const tail = text.slice(bestStart + bestLen).join(":")
|
|
142
|
+
return `${head}::${tail}`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** `canonicalIp("::ffff:127.0.0.1")` is `"127.0.0.1"`; `null` when the input is not an address. */
|
|
146
|
+
export function canonicalIp(input: string): string | null {
|
|
147
|
+
const addr = parseIp(input)
|
|
148
|
+
return addr === null ? null : formatIp(addr)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Parses an address or CIDR rule (`10.0.0.0/8`, `2001:db8::/32`, `203.0.113.7`). Strict: no
|
|
153
|
+
* port, no zone, a prefix length that fits the family. An IPv4-mapped IPv6 range of `/96` or
|
|
154
|
+
* longer is the IPv4 range it maps. Returns `null` when the rule is invalid.
|
|
155
|
+
*/
|
|
156
|
+
export function parseIpRange(rule: string): IpRange | null {
|
|
157
|
+
const s = rule.trim()
|
|
158
|
+
const slash = s.indexOf("/")
|
|
159
|
+
const ipText = slash === -1 ? s : s.slice(0, slash)
|
|
160
|
+
if (ipText.startsWith("[") || ipText.includes("%")) return null
|
|
161
|
+
let bits: number | null = null
|
|
162
|
+
if (slash !== -1) {
|
|
163
|
+
const bitsText = s.slice(slash + 1)
|
|
164
|
+
if (!/^\d{1,3}$/.test(bitsText)) return null
|
|
165
|
+
bits = Number(bitsText)
|
|
166
|
+
}
|
|
167
|
+
if (ipText.includes(":")) {
|
|
168
|
+
const n = parseIpv6(ipText)
|
|
169
|
+
if (n === null) return null
|
|
170
|
+
const prefix = bits ?? 128
|
|
171
|
+
if (prefix > 128) return null
|
|
172
|
+
if ((n & V4_MAPPED_MASK) === V4_MAPPED_PREFIX && prefix >= 96) {
|
|
173
|
+
return v4Range(Number(n & 0xffffffffn), prefix - 96)
|
|
174
|
+
}
|
|
175
|
+
const mask = prefix === 0 ? 0n : (((1n << BigInt(prefix)) - 1n) << BigInt(128 - prefix)) & ((1n << 128n) - 1n)
|
|
176
|
+
return { base: n & mask, mask, v: 6 }
|
|
177
|
+
}
|
|
178
|
+
const n = parseIpv4(ipText)
|
|
179
|
+
if (n === null) return null
|
|
180
|
+
const prefix = bits ?? 32
|
|
181
|
+
if (prefix > 32) return null
|
|
182
|
+
return v4Range(n, prefix)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function v4Range(n: number, prefix: number): IpRange {
|
|
186
|
+
const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0
|
|
187
|
+
return { base: (n & mask) >>> 0, mask, v: 4 }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function ipInRange(addr: IpAddress, range: IpRange): boolean {
|
|
191
|
+
if (addr.v === 4 && range.v === 4) return (addr.n & range.mask) >>> 0 === range.base
|
|
192
|
+
if (addr.v === 6 && range.v === 6) return (addr.n & range.mask) === range.base
|
|
193
|
+
return false
|
|
194
|
+
}
|