@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/client-go/runtime.go
CHANGED
|
@@ -10,8 +10,16 @@ import (
|
|
|
10
10
|
"errors"
|
|
11
11
|
"fmt"
|
|
12
12
|
"io"
|
|
13
|
+
"math"
|
|
14
|
+
"mime/multipart"
|
|
13
15
|
"net/http"
|
|
16
|
+
"net/textproto"
|
|
14
17
|
"net/url"
|
|
18
|
+
"reflect"
|
|
19
|
+
"sort"
|
|
20
|
+
"strconv"
|
|
21
|
+
"strings"
|
|
22
|
+
"sync"
|
|
15
23
|
"time"
|
|
16
24
|
)
|
|
17
25
|
|
|
@@ -48,21 +56,102 @@ type ResponseContext struct {
|
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
// Config holds all configuration for the SDK client.
|
|
59
|
+
//
|
|
60
|
+
// Timeout bounds a whole regular request, including reading the response body.
|
|
61
|
+
// Streams (SSE, WebSocket, realtime) are not bounded by it. Zero means 30s; a
|
|
62
|
+
// negative value disables the timeout.
|
|
63
|
+
//
|
|
64
|
+
// HTTPClient defaults to a client that does not follow redirects to another
|
|
65
|
+
// host (the 3xx response is returned as an error instead), so custom auth
|
|
66
|
+
// headers never reach a different origin.
|
|
67
|
+
//
|
|
68
|
+
// MaxResponseBytes caps a buffered response body. Zero means 100 MiB; a
|
|
69
|
+
// negative value disables the cap.
|
|
51
70
|
type Config struct {
|
|
52
|
-
BaseURL
|
|
53
|
-
BearerToken
|
|
54
|
-
Headers
|
|
55
|
-
Timeout
|
|
56
|
-
ThrowOnError
|
|
57
|
-
StaleMaxEntries
|
|
58
|
-
Invalidation
|
|
59
|
-
OnAuthExpired
|
|
60
|
-
HTTPClient
|
|
61
|
-
AuthHeaderName
|
|
71
|
+
BaseURL string
|
|
72
|
+
BearerToken string
|
|
73
|
+
Headers map[string]string
|
|
74
|
+
Timeout time.Duration
|
|
75
|
+
ThrowOnError bool
|
|
76
|
+
StaleMaxEntries int
|
|
77
|
+
Invalidation *InvalidationConfig
|
|
78
|
+
OnAuthExpired func(context.Context) (string, error)
|
|
79
|
+
HTTPClient *http.Client
|
|
80
|
+
AuthHeaderName string
|
|
62
81
|
AuthHeaderPrefix string
|
|
63
|
-
OnRequest
|
|
64
|
-
OnResponse
|
|
65
|
-
OnLog
|
|
82
|
+
OnRequest []func(*RequestContext) error
|
|
83
|
+
OnResponse []func(*ResponseContext) error
|
|
84
|
+
OnLog func(LogEntry)
|
|
85
|
+
MaxResponseBytes int64
|
|
86
|
+
|
|
87
|
+
auth *authState
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const (
|
|
91
|
+
defaultTimeout = 30 * time.Second
|
|
92
|
+
defaultMaxResponseBytes = 100 << 20
|
|
93
|
+
maxErrorMessageRunes = 512
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
// authState holds the current bearer token so a refreshed token is reused by
|
|
97
|
+
// later calls, and serializes refreshes so N concurrent 401s refresh once.
|
|
98
|
+
type authState struct {
|
|
99
|
+
mu sync.Mutex
|
|
100
|
+
token string
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func (a *authState) current(cfg Config) string {
|
|
104
|
+
if a == nil {
|
|
105
|
+
return cfg.BearerToken
|
|
106
|
+
}
|
|
107
|
+
a.mu.Lock()
|
|
108
|
+
defer a.mu.Unlock()
|
|
109
|
+
return a.token
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// refresh calls OnAuthExpired unless another call already replaced rejected.
|
|
113
|
+
func (a *authState) refresh(ctx context.Context, cfg Config, rejected string) (string, error) {
|
|
114
|
+
if a == nil {
|
|
115
|
+
return cfg.OnAuthExpired(ctx)
|
|
116
|
+
}
|
|
117
|
+
a.mu.Lock()
|
|
118
|
+
defer a.mu.Unlock()
|
|
119
|
+
if a.token != rejected {
|
|
120
|
+
return a.token, nil
|
|
121
|
+
}
|
|
122
|
+
token, err := cfg.OnAuthExpired(ctx)
|
|
123
|
+
if err != nil {
|
|
124
|
+
return "", err
|
|
125
|
+
}
|
|
126
|
+
a.token = token
|
|
127
|
+
return token, nil
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// prepareConfig fills defaults. NewClient calls it once.
|
|
131
|
+
func prepareConfig(cfg Config) Config {
|
|
132
|
+
if cfg.HTTPClient == nil {
|
|
133
|
+
cfg.HTTPClient = &http.Client{CheckRedirect: sameHostRedirects}
|
|
134
|
+
}
|
|
135
|
+
if cfg.Timeout == 0 {
|
|
136
|
+
cfg.Timeout = defaultTimeout
|
|
137
|
+
}
|
|
138
|
+
if cfg.MaxResponseBytes == 0 {
|
|
139
|
+
cfg.MaxResponseBytes = defaultMaxResponseBytes
|
|
140
|
+
}
|
|
141
|
+
cfg.auth = &authState{token: cfg.BearerToken}
|
|
142
|
+
return cfg
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// sameHostRedirects follows redirects on the same scheme and host only.
|
|
146
|
+
func sameHostRedirects(req *http.Request, via []*http.Request) error {
|
|
147
|
+
if len(via) >= 10 {
|
|
148
|
+
return errors.New("honey: stopped after 10 redirects")
|
|
149
|
+
}
|
|
150
|
+
first := via[0].URL
|
|
151
|
+
if req.URL.Scheme != first.Scheme || req.URL.Host != first.Host {
|
|
152
|
+
return http.ErrUseLastResponse
|
|
153
|
+
}
|
|
154
|
+
return nil
|
|
66
155
|
}
|
|
67
156
|
|
|
68
157
|
// LogEntry is the payload passed to Config.OnLog. Consumers plug in their own
|
|
@@ -116,6 +205,395 @@ type requestResult struct {
|
|
|
116
205
|
resp *http.Response
|
|
117
206
|
}
|
|
118
207
|
|
|
208
|
+
// requestBody is one request payload. JSON values and byte payloads can be
|
|
209
|
+
// sent again on an auth retry; a Reader cannot.
|
|
210
|
+
type requestBody struct {
|
|
211
|
+
json any
|
|
212
|
+
hasJSON bool
|
|
213
|
+
raw []byte
|
|
214
|
+
hasRaw bool
|
|
215
|
+
reader io.Reader
|
|
216
|
+
contentType string
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
func noBody() requestBody { return requestBody{} }
|
|
220
|
+
|
|
221
|
+
func jsonBody(v any) requestBody { return requestBody{json: v, hasJSON: true} }
|
|
222
|
+
|
|
223
|
+
func readerBody(r io.Reader, contentType string) requestBody {
|
|
224
|
+
return requestBody{reader: r, contentType: contentType}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
func formBody(values url.Values) requestBody {
|
|
228
|
+
return requestBody{raw: []byte(values.Encode()), hasRaw: true, contentType: "application/x-www-form-urlencoded"}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
func rawBody(raw []byte, contentType string) requestBody {
|
|
232
|
+
return requestBody{raw: raw, hasRaw: true, contentType: contentType}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// setHeaderValue sets a header parameter: nil and nil pointers are skipped,
|
|
236
|
+
// slices are joined with commas.
|
|
237
|
+
func setHeaderValue(h map[string]string, name string, v any) {
|
|
238
|
+
var q queryList
|
|
239
|
+
q.set(name, v)
|
|
240
|
+
if len(q) == 0 {
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
values := make([]string, 0, len(q))
|
|
244
|
+
for _, p := range q {
|
|
245
|
+
values = append(values, p.value)
|
|
246
|
+
}
|
|
247
|
+
h[name] = strings.Join(values, ",")
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// hasHeader reports whether h has name in any casing.
|
|
251
|
+
func hasHeader(h map[string]string, name string) bool {
|
|
252
|
+
for k := range h {
|
|
253
|
+
if strings.EqualFold(k, name) {
|
|
254
|
+
return true
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
func (b requestBody) present() bool { return b.hasJSON || b.hasRaw || b.reader != nil }
|
|
261
|
+
|
|
262
|
+
func (b requestBody) replayable() bool { return b.reader == nil }
|
|
263
|
+
|
|
264
|
+
// bytes returns the encoded payload for JSON and raw bodies.
|
|
265
|
+
func (b requestBody) bytes() ([]byte, error) {
|
|
266
|
+
if b.hasRaw {
|
|
267
|
+
return b.raw, nil
|
|
268
|
+
}
|
|
269
|
+
if b.hasJSON {
|
|
270
|
+
encoded, err := json.Marshal(b.json)
|
|
271
|
+
if err != nil {
|
|
272
|
+
return nil, fmt.Errorf("honey: marshal body: %w", err)
|
|
273
|
+
}
|
|
274
|
+
return encoded, nil
|
|
275
|
+
}
|
|
276
|
+
return nil, nil
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// FilePart is one file in a multipart/form-data upload. The SDK reads Data
|
|
280
|
+
// fully before sending, so the request can be retried after an auth refresh.
|
|
281
|
+
type FilePart struct {
|
|
282
|
+
Filename string
|
|
283
|
+
ContentType string
|
|
284
|
+
Data io.Reader
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
type multipartField struct {
|
|
288
|
+
name string
|
|
289
|
+
value string
|
|
290
|
+
file *FilePart
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
func textField(name string, value any) multipartField {
|
|
294
|
+
return multipartField{name: name, value: formatQueryValue(value)}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func fileField(name string, file FilePart) multipartField {
|
|
298
|
+
return multipartField{name: name, file: &file}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
func multipartBody(fields []multipartField) (requestBody, error) {
|
|
302
|
+
var buf bytes.Buffer
|
|
303
|
+
w := multipart.NewWriter(&buf)
|
|
304
|
+
for _, f := range fields {
|
|
305
|
+
if f.file == nil {
|
|
306
|
+
if err := w.WriteField(f.name, f.value); err != nil {
|
|
307
|
+
return requestBody{}, err
|
|
308
|
+
}
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
h := make(textproto.MIMEHeader)
|
|
312
|
+
filename := f.file.Filename
|
|
313
|
+
if filename == "" {
|
|
314
|
+
filename = f.name
|
|
315
|
+
}
|
|
316
|
+
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name=%s; filename=%s`, strconv.Quote(f.name), strconv.Quote(filename)))
|
|
317
|
+
ct := f.file.ContentType
|
|
318
|
+
if ct == "" {
|
|
319
|
+
ct = "application/octet-stream"
|
|
320
|
+
}
|
|
321
|
+
h.Set("Content-Type", ct)
|
|
322
|
+
part, err := w.CreatePart(h)
|
|
323
|
+
if err != nil {
|
|
324
|
+
return requestBody{}, err
|
|
325
|
+
}
|
|
326
|
+
if f.file.Data != nil {
|
|
327
|
+
if _, err := io.Copy(part, f.file.Data); err != nil {
|
|
328
|
+
return requestBody{}, err
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if err := w.Close(); err != nil {
|
|
333
|
+
return requestBody{}, err
|
|
334
|
+
}
|
|
335
|
+
return requestBody{raw: buf.Bytes(), hasRaw: true, contentType: w.FormDataContentType()}, nil
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// PathParamError reports a path parameter that is missing, or that would
|
|
339
|
+
// change the request path once a URL parser normalizes it ("", "." and "..").
|
|
340
|
+
type PathParamError struct {
|
|
341
|
+
Name string
|
|
342
|
+
Value string
|
|
343
|
+
Missing bool
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
func (e *PathParamError) Error() string {
|
|
347
|
+
if e.Missing {
|
|
348
|
+
return "Missing path param: " + e.Name
|
|
349
|
+
}
|
|
350
|
+
return fmt.Sprintf("Invalid path param %q: %q is not a path segment", e.Name, e.Value)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
func percentEncode(s string, keep func(c byte) bool, spaceAsPlus bool) string {
|
|
354
|
+
const hex = "0123456789ABCDEF"
|
|
355
|
+
var b strings.Builder
|
|
356
|
+
for i := 0; i < len(s); i++ {
|
|
357
|
+
c := s[i]
|
|
358
|
+
switch {
|
|
359
|
+
case (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || keep(c):
|
|
360
|
+
b.WriteByte(c)
|
|
361
|
+
case c == ' ' && spaceAsPlus:
|
|
362
|
+
b.WriteByte('+')
|
|
363
|
+
default:
|
|
364
|
+
b.WriteByte('%')
|
|
365
|
+
b.WriteByte(hex[c>>4])
|
|
366
|
+
b.WriteByte(hex[c&0x0f])
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return b.String()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// escapePathSegment encodes like JavaScript's encodeURIComponent, so every
|
|
373
|
+
// SDK language (and the TypeScript client) sends the same path bytes.
|
|
374
|
+
func escapePathSegment(s string) string {
|
|
375
|
+
return percentEncode(s, func(c byte) bool { return strings.IndexByte("-_.!~*'()", c) >= 0 }, false)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// escapeQueryComponent encodes like URLSearchParams (space becomes '+').
|
|
379
|
+
func escapeQueryComponent(s string) string {
|
|
380
|
+
return percentEncode(s, func(c byte) bool { return strings.IndexByte("*-._", c) >= 0 }, true)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
func encodeSegmentValue(name, value string) (string, error) {
|
|
384
|
+
if value == "" || value == "." || value == ".." {
|
|
385
|
+
return "", &PathParamError{Name: name, Value: value}
|
|
386
|
+
}
|
|
387
|
+
return escapePathSegment(value), nil
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// expandPath fills a path template, segment by segment, with the same grammar
|
|
391
|
+
// as the TypeScript client: `:name` (or `:name?`) takes a whole segment,
|
|
392
|
+
// `*name` (or `*`) as the last segment takes the rest of the path, `{name}`
|
|
393
|
+
// may appear anywhere inside a segment. Values are encoded; values that a URL
|
|
394
|
+
// parser would collapse ("", ".", "..") are rejected.
|
|
395
|
+
func expandPath(template string, params map[string]string) (string, error) {
|
|
396
|
+
segments := strings.Split(template, "/")
|
|
397
|
+
out := make([]string, 0, len(segments))
|
|
398
|
+
for i, seg := range segments {
|
|
399
|
+
switch {
|
|
400
|
+
case len(seg) > 1 && seg[0] == ':':
|
|
401
|
+
optional := strings.HasSuffix(seg, "?")
|
|
402
|
+
name := strings.TrimSuffix(seg[1:], "?")
|
|
403
|
+
if !optional {
|
|
404
|
+
name = seg[1:]
|
|
405
|
+
}
|
|
406
|
+
value, ok := params[name]
|
|
407
|
+
if !ok {
|
|
408
|
+
if optional {
|
|
409
|
+
continue
|
|
410
|
+
}
|
|
411
|
+
return "", &PathParamError{Name: name, Missing: true}
|
|
412
|
+
}
|
|
413
|
+
enc, err := encodeSegmentValue(name, value)
|
|
414
|
+
if err != nil {
|
|
415
|
+
return "", err
|
|
416
|
+
}
|
|
417
|
+
out = append(out, enc)
|
|
418
|
+
case len(seg) > 0 && seg[0] == '*' && i == len(segments)-1:
|
|
419
|
+
name := "*"
|
|
420
|
+
if len(seg) > 1 {
|
|
421
|
+
name = seg[1:]
|
|
422
|
+
}
|
|
423
|
+
value, ok := params[name]
|
|
424
|
+
if !ok {
|
|
425
|
+
if len(seg) == 1 {
|
|
426
|
+
out = append(out, "")
|
|
427
|
+
continue
|
|
428
|
+
}
|
|
429
|
+
return "", &PathParamError{Name: name, Missing: true}
|
|
430
|
+
}
|
|
431
|
+
if value == "" {
|
|
432
|
+
out = append(out, "")
|
|
433
|
+
continue
|
|
434
|
+
}
|
|
435
|
+
parts := strings.Split(value, "/")
|
|
436
|
+
for j, part := range parts {
|
|
437
|
+
enc, err := encodeSegmentValue(name, part)
|
|
438
|
+
if err != nil {
|
|
439
|
+
return "", err
|
|
440
|
+
}
|
|
441
|
+
parts[j] = enc
|
|
442
|
+
}
|
|
443
|
+
out = append(out, strings.Join(parts, "/"))
|
|
444
|
+
default:
|
|
445
|
+
var b strings.Builder
|
|
446
|
+
rest := seg
|
|
447
|
+
for {
|
|
448
|
+
open := strings.IndexByte(rest, '{')
|
|
449
|
+
end := -1
|
|
450
|
+
if open >= 0 {
|
|
451
|
+
end = strings.IndexByte(rest[open:], '}')
|
|
452
|
+
}
|
|
453
|
+
if open < 0 || end < 0 || strings.ContainsRune(rest[open+1:open+end], '{') {
|
|
454
|
+
b.WriteString(rest)
|
|
455
|
+
break
|
|
456
|
+
}
|
|
457
|
+
name := rest[open+1 : open+end]
|
|
458
|
+
value, ok := params[name]
|
|
459
|
+
if !ok {
|
|
460
|
+
return "", &PathParamError{Name: name, Missing: true}
|
|
461
|
+
}
|
|
462
|
+
enc, err := encodeSegmentValue(name, value)
|
|
463
|
+
if err != nil {
|
|
464
|
+
return "", err
|
|
465
|
+
}
|
|
466
|
+
b.WriteString(rest[:open])
|
|
467
|
+
b.WriteString(enc)
|
|
468
|
+
rest = rest[open+end+1:]
|
|
469
|
+
}
|
|
470
|
+
out = append(out, b.String())
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return strings.Join(out, "/"), nil
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// queryPair is one key=value in send order.
|
|
477
|
+
type queryPair struct {
|
|
478
|
+
key string
|
|
479
|
+
value string
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
type queryList []queryPair
|
|
483
|
+
|
|
484
|
+
// set appends v under key: nil and nil pointers are skipped, slices repeat the key.
|
|
485
|
+
func (q *queryList) set(key string, v any) {
|
|
486
|
+
rv := reflect.ValueOf(v)
|
|
487
|
+
for rv.IsValid() && (rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface) {
|
|
488
|
+
if rv.IsNil() {
|
|
489
|
+
return
|
|
490
|
+
}
|
|
491
|
+
rv = rv.Elem()
|
|
492
|
+
}
|
|
493
|
+
if !rv.IsValid() {
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
if (rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array) && rv.Type().Elem().Kind() != reflect.Uint8 {
|
|
497
|
+
for i := 0; i < rv.Len(); i++ {
|
|
498
|
+
q.set(key, rv.Index(i).Interface())
|
|
499
|
+
}
|
|
500
|
+
return
|
|
501
|
+
}
|
|
502
|
+
*q = append(*q, queryPair{key: key, value: formatQueryValue(rv.Interface())})
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
func (q queryList) encode() string {
|
|
506
|
+
parts := make([]string, 0, len(q))
|
|
507
|
+
for _, p := range q {
|
|
508
|
+
parts = append(parts, escapeQueryComponent(p.key)+"="+escapeQueryComponent(p.value))
|
|
509
|
+
}
|
|
510
|
+
return strings.Join(parts, "&")
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// formatQueryValue renders a scalar the way every SDK language does: strings
|
|
514
|
+
// as-is, booleans as true/false, numbers in shortest decimal form, anything
|
|
515
|
+
// else as JSON.
|
|
516
|
+
func formatQueryValue(v any) string {
|
|
517
|
+
rv := reflect.ValueOf(v)
|
|
518
|
+
switch rv.Kind() {
|
|
519
|
+
case reflect.String:
|
|
520
|
+
return rv.String()
|
|
521
|
+
case reflect.Bool:
|
|
522
|
+
return strconv.FormatBool(rv.Bool())
|
|
523
|
+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
524
|
+
return strconv.FormatInt(rv.Int(), 10)
|
|
525
|
+
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
526
|
+
return strconv.FormatUint(rv.Uint(), 10)
|
|
527
|
+
case reflect.Float32, reflect.Float64:
|
|
528
|
+
return formatFloatJS(rv.Float())
|
|
529
|
+
}
|
|
530
|
+
encoded, err := json.Marshal(v)
|
|
531
|
+
if err != nil {
|
|
532
|
+
return fmt.Sprint(v)
|
|
533
|
+
}
|
|
534
|
+
return string(encoded)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// formatFloatJS renders a float like JavaScript's Number#toString.
|
|
538
|
+
func formatFloatJS(f float64) string {
|
|
539
|
+
switch {
|
|
540
|
+
case math.IsNaN(f):
|
|
541
|
+
return "NaN"
|
|
542
|
+
case math.IsInf(f, 1):
|
|
543
|
+
return "Infinity"
|
|
544
|
+
case math.IsInf(f, -1):
|
|
545
|
+
return "-Infinity"
|
|
546
|
+
case f == 0:
|
|
547
|
+
return "0"
|
|
548
|
+
}
|
|
549
|
+
abs := math.Abs(f)
|
|
550
|
+
if abs >= 1e-6 && abs < 1e21 {
|
|
551
|
+
return strconv.FormatFloat(f, 'f', -1, 64)
|
|
552
|
+
}
|
|
553
|
+
s := strconv.FormatFloat(f, 'e', -1, 64)
|
|
554
|
+
mantissa, exp, _ := strings.Cut(s, "e")
|
|
555
|
+
n, _ := strconv.Atoi(exp)
|
|
556
|
+
sign := "+"
|
|
557
|
+
if n < 0 {
|
|
558
|
+
sign = "-"
|
|
559
|
+
n = -n
|
|
560
|
+
}
|
|
561
|
+
return mantissa + "e" + sign + strconv.Itoa(n)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// buildURL joins base and an already-escaped path, keeping the base path and
|
|
565
|
+
// base query, then appends query pairs.
|
|
566
|
+
func buildURL(baseURL, path string, query queryList) (string, error) {
|
|
567
|
+
base, err := url.Parse(baseURL)
|
|
568
|
+
if err != nil {
|
|
569
|
+
return "", err
|
|
570
|
+
}
|
|
571
|
+
if base.Scheme == "" || base.Host == "" {
|
|
572
|
+
return "", fmt.Errorf("honey: base URL %q needs a scheme and host", baseURL)
|
|
573
|
+
}
|
|
574
|
+
if !strings.HasPrefix(path, "/") {
|
|
575
|
+
path = "/" + path
|
|
576
|
+
}
|
|
577
|
+
out := *base
|
|
578
|
+
out.Fragment = ""
|
|
579
|
+
out.RawFragment = ""
|
|
580
|
+
joined := strings.TrimRight(base.EscapedPath(), "/") + path
|
|
581
|
+
unescaped, err := url.PathUnescape(joined)
|
|
582
|
+
if err != nil {
|
|
583
|
+
return "", err
|
|
584
|
+
}
|
|
585
|
+
out.Path = unescaped
|
|
586
|
+
out.RawPath = joined
|
|
587
|
+
extra := query.encode()
|
|
588
|
+
switch {
|
|
589
|
+
case out.RawQuery == "":
|
|
590
|
+
out.RawQuery = extra
|
|
591
|
+
case extra != "":
|
|
592
|
+
out.RawQuery = out.RawQuery + "&" + extra
|
|
593
|
+
}
|
|
594
|
+
return out.String(), nil
|
|
595
|
+
}
|
|
596
|
+
|
|
119
597
|
// doRequest executes an HTTP request, injects auth, merges headers, fires
|
|
120
598
|
// OnRequest/OnResponse hooks in declaration order, handles OnAuthExpired retry
|
|
121
599
|
// on 401, and wraps context cancellation errors. Emits OnLog lifecycle entries
|
|
@@ -126,11 +604,9 @@ func doRequest(
|
|
|
126
604
|
ctx context.Context,
|
|
127
605
|
cfg Config,
|
|
128
606
|
method string,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
bodyReader io.Reader,
|
|
133
|
-
contentType string,
|
|
607
|
+
path string,
|
|
608
|
+
query queryList,
|
|
609
|
+
body requestBody,
|
|
134
610
|
callHeaders map[string]string,
|
|
135
611
|
operation string,
|
|
136
612
|
requestMeta *RequestMeta,
|
|
@@ -152,6 +628,11 @@ func doRequest(
|
|
|
152
628
|
if ctx.Err() != nil {
|
|
153
629
|
return nil, fmt.Errorf("honey: request canceled: %w", ctx.Err())
|
|
154
630
|
}
|
|
631
|
+
if cfg.Timeout > 0 {
|
|
632
|
+
var cancel context.CancelFunc
|
|
633
|
+
ctx, cancel = context.WithTimeout(ctx, cfg.Timeout)
|
|
634
|
+
defer cancel()
|
|
635
|
+
}
|
|
155
636
|
|
|
156
637
|
emitLog(cfg, LogEntry{
|
|
157
638
|
Level: "debug",
|
|
@@ -159,23 +640,23 @@ func doRequest(
|
|
|
159
640
|
Operation: operation,
|
|
160
641
|
})
|
|
161
642
|
|
|
162
|
-
|
|
643
|
+
token := cfg.auth.current(cfg)
|
|
644
|
+
result, err = executeRequest(ctx, cfg, method, path, query, body, callHeaders, token, false, requestMeta)
|
|
163
645
|
if err != nil {
|
|
164
646
|
return nil, err
|
|
165
647
|
}
|
|
166
648
|
|
|
167
|
-
if result.status == 401 && cfg.OnAuthExpired != nil {
|
|
168
|
-
newToken, authErr := cfg.
|
|
649
|
+
if result.status == 401 && cfg.OnAuthExpired != nil && body.replayable() {
|
|
650
|
+
newToken, authErr := cfg.auth.refresh(ctx, cfg, token)
|
|
169
651
|
if authErr != nil {
|
|
170
652
|
err = fmt.Errorf("honey: OnAuthExpired failed: %w", authErr)
|
|
171
653
|
return nil, err
|
|
172
654
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return nil, err
|
|
655
|
+
if newToken != "" {
|
|
656
|
+
result, err = executeRequest(ctx, cfg, method, path, query, body, callHeaders, newToken, true, requestMeta)
|
|
657
|
+
if err != nil {
|
|
658
|
+
return nil, err
|
|
659
|
+
}
|
|
179
660
|
}
|
|
180
661
|
}
|
|
181
662
|
|
|
@@ -200,44 +681,50 @@ func doRequest(
|
|
|
200
681
|
return result, nil
|
|
201
682
|
}
|
|
202
683
|
|
|
203
|
-
func
|
|
684
|
+
func wrapTransportError(ctx context.Context, err error) error {
|
|
685
|
+
if ctx.Err() != nil {
|
|
686
|
+
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
|
687
|
+
return fmt.Errorf("honey: request timed out: %w", ctx.Err())
|
|
688
|
+
}
|
|
689
|
+
if errors.Is(ctx.Err(), context.Canceled) {
|
|
690
|
+
return fmt.Errorf("honey: request canceled: %w", ctx.Err())
|
|
691
|
+
}
|
|
692
|
+
return fmt.Errorf("honey: context error: %w", ctx.Err())
|
|
693
|
+
}
|
|
694
|
+
return fmt.Errorf("honey: http do: %w", err)
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// newRequest builds the outgoing request after running the OnRequest hooks.
|
|
698
|
+
func newRequest(
|
|
204
699
|
ctx context.Context,
|
|
205
700
|
cfg Config,
|
|
206
701
|
method string,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
bodyReader io.Reader,
|
|
211
|
-
contentType string,
|
|
702
|
+
path string,
|
|
703
|
+
query queryList,
|
|
704
|
+
body requestBody,
|
|
212
705
|
callHeaders map[string]string,
|
|
213
706
|
bearerToken string,
|
|
214
707
|
isRetry bool,
|
|
215
708
|
requestMeta *RequestMeta,
|
|
216
|
-
) (*
|
|
217
|
-
fullURL, err := buildURL(cfg.BaseURL,
|
|
709
|
+
) (*http.Request, error) {
|
|
710
|
+
fullURL, err := buildURL(cfg.BaseURL, path, query)
|
|
218
711
|
if err != nil {
|
|
219
712
|
return nil, fmt.Errorf("honey: build URL: %w", err)
|
|
220
713
|
}
|
|
221
714
|
|
|
222
|
-
/*
|
|
223
|
-
*
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
encoded, encErr := json.Marshal(bodyData)
|
|
228
|
-
if encErr != nil {
|
|
229
|
-
return nil, fmt.Errorf("honey: marshal body: %w", encErr)
|
|
230
|
-
}
|
|
231
|
-
bodyBytes = encoded
|
|
715
|
+
/* Reader bodies stream as-is; RequestContext.Body stays nil for them so hook
|
|
716
|
+
* consumers don't try to read or re-hash a single-shot reader. */
|
|
717
|
+
bodyBytes, err := body.bytes()
|
|
718
|
+
if err != nil {
|
|
719
|
+
return nil, err
|
|
232
720
|
}
|
|
233
|
-
hasBody := bodyReader != nil || bodyData != nil
|
|
234
721
|
|
|
235
|
-
headers := mergeHeaders(cfg, callHeaders, bearerToken,
|
|
722
|
+
headers := mergeHeaders(cfg, callHeaders, bearerToken, body.present(), body.contentType)
|
|
236
723
|
|
|
237
724
|
reqCtx := &RequestContext{
|
|
238
725
|
Method: method,
|
|
239
726
|
URL: fullURL,
|
|
240
|
-
Path:
|
|
727
|
+
Path: path,
|
|
241
728
|
Headers: headers,
|
|
242
729
|
Body: bodyBytes,
|
|
243
730
|
IsRetry: isRetry,
|
|
@@ -255,8 +742,8 @@ func executeRequest(
|
|
|
255
742
|
}
|
|
256
743
|
|
|
257
744
|
var reader io.Reader
|
|
258
|
-
if
|
|
259
|
-
reader =
|
|
745
|
+
if body.reader != nil {
|
|
746
|
+
reader = body.reader
|
|
260
747
|
} else if reqCtx.Body != nil {
|
|
261
748
|
reader = bytes.NewReader(reqCtx.Body)
|
|
262
749
|
}
|
|
@@ -266,31 +753,41 @@ func executeRequest(
|
|
|
266
753
|
return nil, fmt.Errorf("honey: build request: %w", err)
|
|
267
754
|
}
|
|
268
755
|
req.Header = reqCtx.Headers
|
|
756
|
+
return req, nil
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
func executeRequest(
|
|
760
|
+
ctx context.Context,
|
|
761
|
+
cfg Config,
|
|
762
|
+
method string,
|
|
763
|
+
path string,
|
|
764
|
+
query queryList,
|
|
765
|
+
body requestBody,
|
|
766
|
+
callHeaders map[string]string,
|
|
767
|
+
bearerToken string,
|
|
768
|
+
isRetry bool,
|
|
769
|
+
requestMeta *RequestMeta,
|
|
770
|
+
) (*requestResult, error) {
|
|
771
|
+
req, err := newRequest(ctx, cfg, method, path, query, body, callHeaders, bearerToken, isRetry, requestMeta)
|
|
772
|
+
if err != nil {
|
|
773
|
+
return nil, err
|
|
774
|
+
}
|
|
269
775
|
|
|
270
776
|
resp, err := cfg.HTTPClient.Do(req)
|
|
271
777
|
if err != nil {
|
|
272
|
-
|
|
273
|
-
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
|
274
|
-
return nil, fmt.Errorf("honey: request timed out: %w", ctx.Err())
|
|
275
|
-
}
|
|
276
|
-
if errors.Is(ctx.Err(), context.Canceled) {
|
|
277
|
-
return nil, fmt.Errorf("honey: request canceled: %w", ctx.Err())
|
|
278
|
-
}
|
|
279
|
-
return nil, fmt.Errorf("honey: context error: %w", ctx.Err())
|
|
280
|
-
}
|
|
281
|
-
return nil, fmt.Errorf("honey: http do: %w", err)
|
|
778
|
+
return nil, wrapTransportError(ctx, err)
|
|
282
779
|
}
|
|
283
780
|
defer resp.Body.Close()
|
|
284
781
|
|
|
285
|
-
|
|
782
|
+
respBody, readErr := readBody(resp.Body, cfg.MaxResponseBytes)
|
|
286
783
|
if readErr != nil {
|
|
287
|
-
return nil,
|
|
784
|
+
return nil, wrapTransportError(ctx, readErr)
|
|
288
785
|
}
|
|
289
786
|
|
|
290
787
|
respCtx := &ResponseContext{
|
|
291
788
|
Status: resp.StatusCode,
|
|
292
789
|
Headers: resp.Header,
|
|
293
|
-
Body:
|
|
790
|
+
Body: respBody,
|
|
294
791
|
IsRetry: isRetry,
|
|
295
792
|
}
|
|
296
793
|
if requestMeta != nil {
|
|
@@ -308,36 +805,88 @@ func executeRequest(
|
|
|
308
805
|
return &requestResult{body: respCtx.Body, status: resp.StatusCode, resp: resp}, nil
|
|
309
806
|
}
|
|
310
807
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
808
|
+
// ErrResponseTooLarge is returned when a buffered body exceeds Config.MaxResponseBytes.
|
|
809
|
+
var ErrResponseTooLarge = errors.New("honey: response body exceeds MaxResponseBytes")
|
|
810
|
+
|
|
811
|
+
func readBody(r io.Reader, limit int64) ([]byte, error) {
|
|
812
|
+
if limit < 0 {
|
|
813
|
+
return io.ReadAll(r)
|
|
315
814
|
}
|
|
316
|
-
|
|
815
|
+
body, err := io.ReadAll(io.LimitReader(r, limit+1))
|
|
317
816
|
if err != nil {
|
|
318
|
-
return
|
|
817
|
+
return nil, err
|
|
319
818
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
819
|
+
if int64(len(body)) > limit {
|
|
820
|
+
return nil, ErrResponseTooLarge
|
|
821
|
+
}
|
|
822
|
+
return body, nil
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// streamClient is the configured client without its overall timeout: an
|
|
826
|
+
// http.Client Timeout also bounds body reads, which would cut every stream.
|
|
827
|
+
func streamClient(cfg Config) *http.Client {
|
|
828
|
+
c := *cfg.HTTPClient
|
|
829
|
+
c.Timeout = 0
|
|
830
|
+
return &c
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// openStream sends a streaming request (SSE) through the same URL, header,
|
|
834
|
+
// auth and hook pipeline as a regular request. Non-2xx statuses become typed
|
|
835
|
+
// errors; the caller owns the returned body.
|
|
836
|
+
func openStream(
|
|
837
|
+
ctx context.Context,
|
|
838
|
+
cfg Config,
|
|
839
|
+
method string,
|
|
840
|
+
path string,
|
|
841
|
+
query queryList,
|
|
842
|
+
body requestBody,
|
|
843
|
+
callHeaders map[string]string,
|
|
844
|
+
) (*http.Response, error) {
|
|
845
|
+
token := cfg.auth.current(cfg)
|
|
846
|
+
for attempt := 0; ; attempt++ {
|
|
847
|
+
req, err := newRequest(ctx, cfg, method, path, query, body, callHeaders, token, attempt > 0, nil)
|
|
848
|
+
if err != nil {
|
|
849
|
+
return nil, err
|
|
850
|
+
}
|
|
851
|
+
resp, err := streamClient(cfg).Do(req)
|
|
852
|
+
if err != nil {
|
|
853
|
+
return nil, wrapTransportError(ctx, err)
|
|
854
|
+
}
|
|
855
|
+
if resp.StatusCode == 401 && attempt == 0 && cfg.OnAuthExpired != nil && body.replayable() {
|
|
856
|
+
errBody, _ := readBody(resp.Body, 64<<10)
|
|
857
|
+
resp.Body.Close()
|
|
858
|
+
newToken, authErr := cfg.auth.refresh(ctx, cfg, token)
|
|
859
|
+
if authErr != nil {
|
|
860
|
+
return nil, fmt.Errorf("honey: OnAuthExpired failed: %w", authErr)
|
|
861
|
+
}
|
|
862
|
+
if newToken == "" {
|
|
863
|
+
return nil, raiseForStatus(resp, errBody)
|
|
326
864
|
}
|
|
865
|
+
token = newToken
|
|
866
|
+
continue
|
|
327
867
|
}
|
|
328
|
-
|
|
868
|
+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
869
|
+
defer resp.Body.Close()
|
|
870
|
+
errBody, _ := readBody(resp.Body, 64<<10)
|
|
871
|
+
return nil, raiseForStatus(resp, errBody)
|
|
872
|
+
}
|
|
873
|
+
return resp, nil
|
|
329
874
|
}
|
|
330
|
-
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// authHeaders returns cfg.Headers plus the auth header, for transports that
|
|
878
|
+
// cannot run the full request pipeline (WebSocket dials).
|
|
879
|
+
func authHeaders(cfg Config, extra map[string]string) http.Header {
|
|
880
|
+
return mergeHeaders(cfg, extra, cfg.auth.current(cfg), false, "")
|
|
331
881
|
}
|
|
332
882
|
|
|
333
883
|
// mergeHeaders builds the final http.Header for a request by merging in order:
|
|
334
|
-
// config Headers → call Headers (per-call wins per key) →
|
|
335
|
-
// hasBody → Authorization when bearerToken non-empty →
|
|
336
|
-
// absent (OnRequest hooks may still overwrite). Returned
|
|
337
|
-
// by the caller and may be mutated freely (e.g. by OnRequest
|
|
338
|
-
// contentType is the explicit Content-Type for the request body; when
|
|
339
|
-
// and hasBody is true, defaults to application/json.
|
|
340
|
-
// application/octet-stream for streaming-upload ops.
|
|
884
|
+
// config Headers → call Headers (per-call wins per key, case-insensitively) →
|
|
885
|
+
// Content-Type when hasBody → Authorization when bearerToken non-empty →
|
|
886
|
+
// X-Request-Id when absent (OnRequest hooks may still overwrite). Returned
|
|
887
|
+
// header is owned by the caller and may be mutated freely (e.g. by OnRequest
|
|
888
|
+
// hooks). contentType is the explicit Content-Type for the request body; when
|
|
889
|
+
// empty and hasBody is true, defaults to application/json.
|
|
341
890
|
func mergeHeaders(
|
|
342
891
|
cfg Config,
|
|
343
892
|
callHeaders map[string]string,
|
|
@@ -353,11 +902,11 @@ func mergeHeaders(
|
|
|
353
902
|
}
|
|
354
903
|
h.Set("Content-Type", ct)
|
|
355
904
|
}
|
|
356
|
-
for
|
|
357
|
-
h.Set(k,
|
|
905
|
+
for _, k := range sortedKeys(cfg.Headers) {
|
|
906
|
+
h.Set(k, cfg.Headers[k])
|
|
358
907
|
}
|
|
359
|
-
for
|
|
360
|
-
h.Set(k,
|
|
908
|
+
for _, k := range sortedKeys(callHeaders) {
|
|
909
|
+
h.Set(k, callHeaders[k])
|
|
361
910
|
}
|
|
362
911
|
if bearerToken != "" {
|
|
363
912
|
name := cfg.AuthHeaderName
|
|
@@ -376,6 +925,15 @@ func mergeHeaders(
|
|
|
376
925
|
return h
|
|
377
926
|
}
|
|
378
927
|
|
|
928
|
+
func sortedKeys(m map[string]string) []string {
|
|
929
|
+
keys := make([]string, 0, len(m))
|
|
930
|
+
for k := range m {
|
|
931
|
+
keys = append(keys, k)
|
|
932
|
+
}
|
|
933
|
+
sort.Strings(keys)
|
|
934
|
+
return keys
|
|
935
|
+
}
|
|
936
|
+
|
|
379
937
|
func unmarshalBody[T any](body []byte) (*T, error) {
|
|
380
938
|
if len(body) == 0 {
|
|
381
939
|
return nil, nil
|