@lovrozagar/honey 0.6.8 → 0.7.1
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 +819 -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 +603 -423
- package/src/types.ts +8 -0
- package/src/validation.ts +221 -96
- package/src/with-headers.ts +45 -0
- package/src/ws/bun.ts +53 -15
- package/src/ws/cloudflare.ts +81 -19
- package/src/ws/deno.ts +124 -36
- package/src/ws/node.ts +173 -79
- package/src/ws/session.ts +73 -0
- package/src/ws/shared.ts +140 -0
- package/src/ws-origin.ts +53 -0
- package/src/yaml.ts +45 -22
- package/dist/client-mcp/server.d.ts +0 -10
- package/dist/client-mcp/server.d.ts.map +0 -1
- package/dist/client-mcp/types.d.ts +0 -16
- package/dist/client-mcp/types.d.ts.map +0 -1
- package/dist/cloudflare/realtime.d.ts +0 -30
- package/dist/cloudflare/realtime.d.ts.map +0 -1
- package/dist/realtime/buffer.d.ts +0 -18
- package/dist/realtime/buffer.d.ts.map +0 -1
- package/dist/realtime/longpoll.d.ts +0 -23
- package/dist/realtime/longpoll.d.ts.map +0 -1
- package/dist/realtime/protocol.d.ts +0 -50
- package/dist/realtime/protocol.d.ts.map +0 -1
- package/dist/realtime/runtime.d.ts +0 -74
- package/dist/realtime/runtime.d.ts.map +0 -1
- package/src/cloudflare/realtime.ts +0 -234
- package/src/realtime/buffer.ts +0 -101
- package/src/realtime/longpoll.ts +0 -148
- package/src/realtime/protocol.ts +0 -128
- package/src/realtime/runtime.ts +0 -427
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import codecs
|
|
4
|
+
from typing import AsyncIterator, Iterable, Iterator, TypedDict
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SSEEvent(TypedDict, total=False):
|
|
10
|
+
data: str
|
|
11
|
+
event: str
|
|
12
|
+
id: str
|
|
13
|
+
retry: int
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DEFAULT_MAX_BUFFER = 1024 * 1024
|
|
17
|
+
MAX_EVENT_DATA = 8 * 1024 * 1024
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SSEParser:
|
|
21
|
+
"""WHATWG event-stream parser.
|
|
22
|
+
|
|
23
|
+
Lines end in CRLF, LF or CR only (never U+2028 and friends, which
|
|
24
|
+
``str.splitlines`` would also split on). A blank line dispatches the event;
|
|
25
|
+
a final event without its blank line is discarded; the id is sticky across
|
|
26
|
+
events; ``retry`` needs ASCII digits. Each chunk is scanned once.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, max_line: int = DEFAULT_MAX_BUFFER) -> None:
|
|
30
|
+
self._max_line = max_line
|
|
31
|
+
self._decoder = codecs.getincrementaldecoder("utf-8")("replace")
|
|
32
|
+
self._line: list[str] = []
|
|
33
|
+
self._line_len = 0
|
|
34
|
+
self._pending_cr = False
|
|
35
|
+
self._first = True
|
|
36
|
+
self._data: list[str] = []
|
|
37
|
+
self._data_len = 0
|
|
38
|
+
self._has_data = False
|
|
39
|
+
self._event: str | None = None
|
|
40
|
+
self._last_id: str | None = None
|
|
41
|
+
self._retry: int | None = None
|
|
42
|
+
|
|
43
|
+
def feed(self, chunk: bytes) -> Iterator[SSEEvent]:
|
|
44
|
+
yield from self._feed_text(self._decoder.decode(chunk))
|
|
45
|
+
|
|
46
|
+
def feed_text(self, text: str) -> Iterator[SSEEvent]:
|
|
47
|
+
yield from self._feed_text(text)
|
|
48
|
+
|
|
49
|
+
def _feed_text(self, text: str) -> Iterator[SSEEvent]:
|
|
50
|
+
start = 0
|
|
51
|
+
for i, ch in enumerate(text):
|
|
52
|
+
if i < start or (ch != "\r" and ch != "\n"):
|
|
53
|
+
continue
|
|
54
|
+
if ch == "\n" and self._pending_cr and i == start:
|
|
55
|
+
# LF completing a CRLF split across chunks
|
|
56
|
+
self._pending_cr = False
|
|
57
|
+
start = i + 1
|
|
58
|
+
continue
|
|
59
|
+
self._pending_cr = False
|
|
60
|
+
self._append(text[start:i])
|
|
61
|
+
line = "".join(self._line)
|
|
62
|
+
self._line = []
|
|
63
|
+
self._line_len = 0
|
|
64
|
+
if ch == "\r":
|
|
65
|
+
if i + 1 < len(text):
|
|
66
|
+
if text[i + 1] == "\n":
|
|
67
|
+
start = i + 2
|
|
68
|
+
event = self._process(line)
|
|
69
|
+
if event is not None:
|
|
70
|
+
yield event
|
|
71
|
+
continue
|
|
72
|
+
else:
|
|
73
|
+
self._pending_cr = True
|
|
74
|
+
start = i + 1
|
|
75
|
+
event = self._process(line)
|
|
76
|
+
if event is not None:
|
|
77
|
+
yield event
|
|
78
|
+
if start < len(text):
|
|
79
|
+
self._pending_cr = False
|
|
80
|
+
self._append(text[start:])
|
|
81
|
+
|
|
82
|
+
def _append(self, part: str) -> None:
|
|
83
|
+
if not part:
|
|
84
|
+
return
|
|
85
|
+
self._line_len += len(part)
|
|
86
|
+
if self._line_len > self._max_line:
|
|
87
|
+
raise RuntimeError(f"SSE line exceeded {self._max_line} characters")
|
|
88
|
+
self._line.append(part)
|
|
89
|
+
|
|
90
|
+
def _process(self, line: str) -> SSEEvent | None:
|
|
91
|
+
if self._first:
|
|
92
|
+
self._first = False
|
|
93
|
+
if line.startswith("\ufeff"):
|
|
94
|
+
line = line[1:]
|
|
95
|
+
if line == "":
|
|
96
|
+
return self._dispatch()
|
|
97
|
+
if line.startswith(":"):
|
|
98
|
+
return None
|
|
99
|
+
name, sep, value = line.partition(":")
|
|
100
|
+
if sep and value.startswith(" "):
|
|
101
|
+
value = value[1:]
|
|
102
|
+
if name == "data":
|
|
103
|
+
self._data_len += len(value) + 1
|
|
104
|
+
if self._data_len > MAX_EVENT_DATA:
|
|
105
|
+
raise RuntimeError(f"SSE event exceeded {MAX_EVENT_DATA} characters")
|
|
106
|
+
self._data.append(value)
|
|
107
|
+
self._has_data = True
|
|
108
|
+
elif name == "event":
|
|
109
|
+
self._event = value
|
|
110
|
+
elif name == "id":
|
|
111
|
+
if "\0" not in value:
|
|
112
|
+
self._last_id = value
|
|
113
|
+
elif name == "retry":
|
|
114
|
+
if value and value.isascii() and value.isdigit():
|
|
115
|
+
self._retry = int(value)
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def _dispatch(self) -> SSEEvent | None:
|
|
119
|
+
event: SSEEvent | None = None
|
|
120
|
+
if self._has_data:
|
|
121
|
+
event = {"data": "\n".join(self._data)}
|
|
122
|
+
if self._event is not None:
|
|
123
|
+
event["event"] = self._event
|
|
124
|
+
if self._last_id is not None:
|
|
125
|
+
event["id"] = self._last_id
|
|
126
|
+
if self._retry is not None:
|
|
127
|
+
event["retry"] = self._retry
|
|
128
|
+
self._data = []
|
|
129
|
+
self._data_len = 0
|
|
130
|
+
self._has_data = False
|
|
131
|
+
self._event = None
|
|
132
|
+
self._retry = None
|
|
133
|
+
return event
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def parse_sse_text(chunks: Iterable[str]) -> list[SSEEvent]:
|
|
137
|
+
"""Parse already-decoded text chunks; used by the conformance vectors."""
|
|
138
|
+
parser = SSEParser()
|
|
139
|
+
out: list[SSEEvent] = []
|
|
140
|
+
for chunk in chunks:
|
|
141
|
+
out.extend(parser.feed_text(chunk))
|
|
142
|
+
return out
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
async def parse_sse_stream(
|
|
146
|
+
response: httpx.Response,
|
|
147
|
+
max_buffer_size: int = DEFAULT_MAX_BUFFER,
|
|
148
|
+
) -> AsyncIterator[SSEEvent]:
|
|
149
|
+
parser = SSEParser(max_buffer_size)
|
|
150
|
+
async for chunk in response.aiter_bytes():
|
|
151
|
+
for event in parser.feed(chunk):
|
|
152
|
+
yield event
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
__all__ = ["SSEEvent", "SSEParser", "parse_sse_stream", "parse_sse_text"]
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import uuid
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Protocol, cast, runtime_checkable
|
|
8
|
+
from urllib.parse import urlencode
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import websockets
|
|
12
|
+
except ImportError:
|
|
13
|
+
websockets = None # type: ignore[assignment]
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import httpx
|
|
17
|
+
except ImportError:
|
|
18
|
+
httpx = None # type: ignore[assignment]
|
|
19
|
+
|
|
20
|
+
from ._sse import parse_sse_stream
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TransportKind(str, Enum):
|
|
24
|
+
WS = "ws"
|
|
25
|
+
SSE = "sse"
|
|
26
|
+
LONGPOLL = "longpoll"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class TransportOpts:
|
|
31
|
+
"""Options for each transport connect.
|
|
32
|
+
|
|
33
|
+
``max_reconnect_attempts`` caps consecutive failed reconnects (0 or less:
|
|
34
|
+
unlimited); ``reconnect_delay_ms`` is the first backoff delay. ``headers``
|
|
35
|
+
are sent on every transport (the generated SDK fills in config headers and
|
|
36
|
+
auth). ``max_message_bytes`` caps one WebSocket message.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
reconnect_token: str | None = None
|
|
40
|
+
last_event_id: str | None = None
|
|
41
|
+
headers: dict[str, str] | None = None
|
|
42
|
+
max_reconnect_attempts: int = 5
|
|
43
|
+
reconnect_delay_ms: int = 1000
|
|
44
|
+
protocols: list[str] | None = None
|
|
45
|
+
max_message_bytes: int = 16 * 1024 * 1024
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@runtime_checkable
|
|
49
|
+
class TransportConn(Protocol):
|
|
50
|
+
async def recv(self) -> Any: ...
|
|
51
|
+
async def send(self, data: Any) -> None: ...
|
|
52
|
+
async def close(self) -> None: ...
|
|
53
|
+
def kind(self) -> TransportKind: ...
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@runtime_checkable
|
|
57
|
+
class TransportAdapter(Protocol):
|
|
58
|
+
def name(self) -> str: ...
|
|
59
|
+
def kind(self) -> TransportKind: ...
|
|
60
|
+
async def connect(self, url: str, opts: TransportOpts) -> TransportConn: ...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class _StubConn:
|
|
64
|
+
"""Shared stub retained from 8.b.2 for backward compat.
|
|
65
|
+
|
|
66
|
+
Real wire conns below (_WsConn/_SseConn/_LongpollConn) supersede it
|
|
67
|
+
inside the default adapters. Still importable for consumer-authored
|
|
68
|
+
tests that stub before the real impls landed.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, kind: TransportKind) -> None:
|
|
72
|
+
self._kind = kind
|
|
73
|
+
|
|
74
|
+
async def recv(self) -> Any:
|
|
75
|
+
raise NotImplementedError(
|
|
76
|
+
"_StubConn.recv is a placeholder; use WsAdapter/SseAdapter/LongpollAdapter",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
async def send(self, data: Any) -> None:
|
|
80
|
+
raise NotImplementedError(
|
|
81
|
+
"_StubConn.send is a placeholder; use WsAdapter/SseAdapter/LongpollAdapter",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
async def close(self) -> None:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
def kind(self) -> TransportKind:
|
|
88
|
+
return self._kind
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _to_ws_url(url: str) -> str:
|
|
92
|
+
if url.startswith("https://"):
|
|
93
|
+
return "wss://" + url[len("https://") :]
|
|
94
|
+
if url.startswith("http://"):
|
|
95
|
+
return "ws://" + url[len("http://") :]
|
|
96
|
+
return url
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class _WsConn:
|
|
100
|
+
def __init__(self, conn: Any) -> None:
|
|
101
|
+
self._conn = conn
|
|
102
|
+
|
|
103
|
+
async def recv(self) -> Any:
|
|
104
|
+
msg = await self._conn.recv()
|
|
105
|
+
if isinstance(msg, (bytes, bytearray)):
|
|
106
|
+
msg = msg.decode("utf-8")
|
|
107
|
+
try:
|
|
108
|
+
return json.loads(msg)
|
|
109
|
+
except (ValueError, TypeError):
|
|
110
|
+
return msg
|
|
111
|
+
|
|
112
|
+
async def send(self, data: Any) -> None:
|
|
113
|
+
if isinstance(data, (str, bytes, bytearray)):
|
|
114
|
+
payload: Any = data
|
|
115
|
+
else:
|
|
116
|
+
payload = json.dumps(data)
|
|
117
|
+
await self._conn.send(payload)
|
|
118
|
+
|
|
119
|
+
async def close(self) -> None:
|
|
120
|
+
try:
|
|
121
|
+
await self._conn.close()
|
|
122
|
+
except Exception:
|
|
123
|
+
""" swallow — close must never raise """
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
def kind(self) -> TransportKind:
|
|
127
|
+
return TransportKind.WS
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class WsAdapter:
|
|
132
|
+
"""Default WebSocket adapter using `websockets.connect`."""
|
|
133
|
+
|
|
134
|
+
subprotocols: list[str] = field(default_factory=list)
|
|
135
|
+
|
|
136
|
+
def name(self) -> str:
|
|
137
|
+
return "ws"
|
|
138
|
+
|
|
139
|
+
def kind(self) -> TransportKind:
|
|
140
|
+
return TransportKind.WS
|
|
141
|
+
|
|
142
|
+
async def connect(self, url: str, opts: TransportOpts) -> TransportConn:
|
|
143
|
+
if websockets is None:
|
|
144
|
+
raise RuntimeError(
|
|
145
|
+
"pip install websockets to use the default WsAdapter",
|
|
146
|
+
)
|
|
147
|
+
ws_url = _to_ws_url(url)
|
|
148
|
+
query: dict[str, str] = {}
|
|
149
|
+
if opts.reconnect_token is not None:
|
|
150
|
+
query["reconnect_token"] = opts.reconnect_token
|
|
151
|
+
if opts.last_event_id is not None:
|
|
152
|
+
query["last_event_id"] = opts.last_event_id
|
|
153
|
+
if query:
|
|
154
|
+
sep = "&" if "?" in ws_url else "?"
|
|
155
|
+
ws_url = f"{ws_url}{sep}{urlencode(query)}"
|
|
156
|
+
subprotocols = opts.protocols or (self.subprotocols or None)
|
|
157
|
+
headers = opts.headers or None
|
|
158
|
+
conn = await websockets.connect(
|
|
159
|
+
ws_url,
|
|
160
|
+
subprotocols=cast(Any, subprotocols),
|
|
161
|
+
additional_headers=headers,
|
|
162
|
+
max_size=opts.max_message_bytes,
|
|
163
|
+
)
|
|
164
|
+
return _WsConn(conn)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class _SseConn:
|
|
168
|
+
def __init__(
|
|
169
|
+
self,
|
|
170
|
+
client: Any,
|
|
171
|
+
owns_client: bool,
|
|
172
|
+
ctx: Any,
|
|
173
|
+
response: Any,
|
|
174
|
+
) -> None:
|
|
175
|
+
self._client = client
|
|
176
|
+
self._owns_client = owns_client
|
|
177
|
+
self._ctx = ctx
|
|
178
|
+
self._response = response
|
|
179
|
+
self._iter = parse_sse_stream(response)
|
|
180
|
+
|
|
181
|
+
async def recv(self) -> Any:
|
|
182
|
+
event = await self._iter.__anext__()
|
|
183
|
+
data = event.get("data")
|
|
184
|
+
if data is None:
|
|
185
|
+
return event
|
|
186
|
+
try:
|
|
187
|
+
return json.loads(data)
|
|
188
|
+
except (ValueError, TypeError):
|
|
189
|
+
return data
|
|
190
|
+
|
|
191
|
+
async def send(self, data: Any) -> None:
|
|
192
|
+
raise RuntimeError("SSE transport is receive-only")
|
|
193
|
+
|
|
194
|
+
async def close(self) -> None:
|
|
195
|
+
try:
|
|
196
|
+
await self._ctx.__aexit__(None, None, None)
|
|
197
|
+
except Exception:
|
|
198
|
+
pass
|
|
199
|
+
if self._owns_client:
|
|
200
|
+
try:
|
|
201
|
+
await self._client.aclose()
|
|
202
|
+
except Exception:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
def kind(self) -> TransportKind:
|
|
206
|
+
return TransportKind.SSE
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@dataclass
|
|
210
|
+
class SseAdapter:
|
|
211
|
+
"""Default SSE adapter using `httpx.AsyncClient.stream`."""
|
|
212
|
+
|
|
213
|
+
http_client: Any | None = None
|
|
214
|
+
|
|
215
|
+
def name(self) -> str:
|
|
216
|
+
return "sse"
|
|
217
|
+
|
|
218
|
+
def kind(self) -> TransportKind:
|
|
219
|
+
return TransportKind.SSE
|
|
220
|
+
|
|
221
|
+
async def connect(self, url: str, opts: TransportOpts) -> TransportConn:
|
|
222
|
+
if httpx is None:
|
|
223
|
+
raise RuntimeError(
|
|
224
|
+
"pip install httpx to use the default SseAdapter",
|
|
225
|
+
)
|
|
226
|
+
params: dict[str, str] = {}
|
|
227
|
+
if opts.reconnect_token is not None:
|
|
228
|
+
params["reconnect_token"] = opts.reconnect_token
|
|
229
|
+
headers = dict(opts.headers or {})
|
|
230
|
+
headers.setdefault("Accept", "text/event-stream")
|
|
231
|
+
if opts.last_event_id is not None:
|
|
232
|
+
headers["Last-Event-ID"] = opts.last_event_id
|
|
233
|
+
if not any(k.lower() == "x-request-id" for k in headers):
|
|
234
|
+
headers["x-request-id"] = str(uuid.uuid4())
|
|
235
|
+
client = self.http_client or httpx.AsyncClient()
|
|
236
|
+
owns_client = self.http_client is None
|
|
237
|
+
# a stream may idle between events: no read timeout
|
|
238
|
+
ctx = client.stream(
|
|
239
|
+
"GET",
|
|
240
|
+
url,
|
|
241
|
+
params=params or None,
|
|
242
|
+
headers=headers,
|
|
243
|
+
timeout=httpx.Timeout(30.0, read=None),
|
|
244
|
+
)
|
|
245
|
+
try:
|
|
246
|
+
response = await ctx.__aenter__()
|
|
247
|
+
except BaseException:
|
|
248
|
+
if owns_client:
|
|
249
|
+
await client.aclose()
|
|
250
|
+
raise
|
|
251
|
+
try:
|
|
252
|
+
response.raise_for_status()
|
|
253
|
+
except BaseException:
|
|
254
|
+
await ctx.__aexit__(None, None, None)
|
|
255
|
+
if owns_client:
|
|
256
|
+
await client.aclose()
|
|
257
|
+
raise
|
|
258
|
+
return _SseConn(
|
|
259
|
+
client=client,
|
|
260
|
+
owns_client=owns_client,
|
|
261
|
+
ctx=ctx,
|
|
262
|
+
response=response,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class _LongpollConn:
|
|
267
|
+
def __init__(
|
|
268
|
+
self,
|
|
269
|
+
client: Any,
|
|
270
|
+
owns_client: bool,
|
|
271
|
+
url: str,
|
|
272
|
+
headers: dict[str, str],
|
|
273
|
+
reconnect_token: str | None,
|
|
274
|
+
last_event_id: str | None,
|
|
275
|
+
) -> None:
|
|
276
|
+
self._client = client
|
|
277
|
+
self._owns_client = owns_client
|
|
278
|
+
self._url = url
|
|
279
|
+
self._headers = headers
|
|
280
|
+
self._reconnect_token = reconnect_token
|
|
281
|
+
self._last_event_id = last_event_id
|
|
282
|
+
self._closed = False
|
|
283
|
+
|
|
284
|
+
async def recv(self) -> Any:
|
|
285
|
+
if self._closed:
|
|
286
|
+
raise RuntimeError("longpoll connection closed")
|
|
287
|
+
params: dict[str, str] = {}
|
|
288
|
+
if self._reconnect_token is not None:
|
|
289
|
+
params["reconnect_token"] = self._reconnect_token
|
|
290
|
+
if self._last_event_id is not None:
|
|
291
|
+
params["last_event_id"] = self._last_event_id
|
|
292
|
+
resp = await self._client.get(
|
|
293
|
+
self._url,
|
|
294
|
+
headers=self._headers or None,
|
|
295
|
+
params=params or None,
|
|
296
|
+
)
|
|
297
|
+
resp.raise_for_status()
|
|
298
|
+
payload: Any
|
|
299
|
+
try:
|
|
300
|
+
payload = resp.json()
|
|
301
|
+
except Exception:
|
|
302
|
+
payload = resp.text
|
|
303
|
+
if isinstance(payload, dict):
|
|
304
|
+
new_token = payload.get("reconnect_token")
|
|
305
|
+
if isinstance(new_token, str):
|
|
306
|
+
self._reconnect_token = new_token
|
|
307
|
+
new_id = payload.get("id") or payload.get("last_event_id")
|
|
308
|
+
if isinstance(new_id, (str, int)):
|
|
309
|
+
self._last_event_id = str(new_id)
|
|
310
|
+
return payload
|
|
311
|
+
|
|
312
|
+
async def send(self, data: Any) -> None:
|
|
313
|
+
raise RuntimeError("longpoll transport is receive-only")
|
|
314
|
+
|
|
315
|
+
async def close(self) -> None:
|
|
316
|
+
self._closed = True
|
|
317
|
+
if self._owns_client:
|
|
318
|
+
try:
|
|
319
|
+
await self._client.aclose()
|
|
320
|
+
except Exception:
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
def kind(self) -> TransportKind:
|
|
324
|
+
return TransportKind.LONGPOLL
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@dataclass
|
|
328
|
+
class LongpollAdapter:
|
|
329
|
+
"""Default long-poll adapter using repeated `httpx.AsyncClient.get`."""
|
|
330
|
+
|
|
331
|
+
http_client: Any | None = None
|
|
332
|
+
poll_timeout: float = 30.0
|
|
333
|
+
|
|
334
|
+
def name(self) -> str:
|
|
335
|
+
return "longpoll"
|
|
336
|
+
|
|
337
|
+
def kind(self) -> TransportKind:
|
|
338
|
+
return TransportKind.LONGPOLL
|
|
339
|
+
|
|
340
|
+
async def connect(self, url: str, opts: TransportOpts) -> TransportConn:
|
|
341
|
+
if httpx is None:
|
|
342
|
+
raise RuntimeError(
|
|
343
|
+
"pip install httpx to use the default LongpollAdapter",
|
|
344
|
+
)
|
|
345
|
+
client = self.http_client or httpx.AsyncClient(timeout=self.poll_timeout)
|
|
346
|
+
owns_client = self.http_client is None
|
|
347
|
+
return _LongpollConn(
|
|
348
|
+
client=client,
|
|
349
|
+
owns_client=owns_client,
|
|
350
|
+
url=url,
|
|
351
|
+
headers=dict(opts.headers or {}),
|
|
352
|
+
reconnect_token=opts.reconnect_token,
|
|
353
|
+
last_event_id=opts.last_event_id,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
__all__ = [
|
|
358
|
+
"LongpollAdapter",
|
|
359
|
+
"SseAdapter",
|
|
360
|
+
"TransportAdapter",
|
|
361
|
+
"TransportConn",
|
|
362
|
+
"TransportKind",
|
|
363
|
+
"TransportOpts",
|
|
364
|
+
"WsAdapter",
|
|
365
|
+
"_StubConn",
|
|
366
|
+
]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, AsyncIterator, Callable, cast
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import websockets
|
|
8
|
+
except ImportError:
|
|
9
|
+
websockets = None # type: ignore[assignment]
|
|
10
|
+
|
|
11
|
+
_VALID_EVENTS = frozenset({"open", "close", "error", "message"})
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _TypedWebSocket:
|
|
15
|
+
"""Async context-manager wrapping a websockets connection.
|
|
16
|
+
|
|
17
|
+
Parity notes (Phase 10 WS audit):
|
|
18
|
+
- Remote close code + reason surfaced via close_code / close_reason after
|
|
19
|
+
__aexit__ or after the iterator terminates; also passed to "close"
|
|
20
|
+
handlers registered via .on().
|
|
21
|
+
- Auth handshake: caller passes query-string params (e.g. ?token=) to the
|
|
22
|
+
emitted method; server rejects via close frame which this class fires
|
|
23
|
+
as a "close" event plus sets close_code.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
url: str,
|
|
29
|
+
protocols: list[str] | None = None,
|
|
30
|
+
extra_headers: dict[str, str] | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
if websockets is None:
|
|
33
|
+
raise ImportError("pip install honey-sdk[ws] to use WebSocket methods")
|
|
34
|
+
self._url = url
|
|
35
|
+
self._protocols = protocols or []
|
|
36
|
+
self._extra_headers = extra_headers or {}
|
|
37
|
+
self._conn: Any = None
|
|
38
|
+
self._handlers: dict[str, list[Callable[..., Any]]] = {
|
|
39
|
+
"open": [],
|
|
40
|
+
"close": [],
|
|
41
|
+
"error": [],
|
|
42
|
+
"message": [],
|
|
43
|
+
}
|
|
44
|
+
self.close_code: int | None = None
|
|
45
|
+
self.close_reason: str | None = None
|
|
46
|
+
self._close_fired = False
|
|
47
|
+
|
|
48
|
+
async def __aenter__(self) -> _TypedWebSocket:
|
|
49
|
+
if websockets is None:
|
|
50
|
+
raise ImportError("pip install honey-sdk[ws] to use WebSocket methods")
|
|
51
|
+
self._conn = await _connect(self._url, self._protocols, self._extra_headers)
|
|
52
|
+
for handler in self._handlers["open"]:
|
|
53
|
+
try:
|
|
54
|
+
handler()
|
|
55
|
+
except Exception:
|
|
56
|
+
pass
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
60
|
+
if self._conn is not None:
|
|
61
|
+
try:
|
|
62
|
+
await self._conn.close()
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
self._capture_close()
|
|
66
|
+
self._fire_close(
|
|
67
|
+
self.close_code if self.close_code is not None else 1000,
|
|
68
|
+
self.close_reason if self.close_reason is not None else "",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def __aiter__(self) -> AsyncIterator[str | bytes]:
|
|
72
|
+
return self._iter_messages()
|
|
73
|
+
|
|
74
|
+
async def _iter_messages(self) -> AsyncIterator[str | bytes]:
|
|
75
|
+
try:
|
|
76
|
+
async for msg in self._conn:
|
|
77
|
+
for handler in self._handlers["message"]:
|
|
78
|
+
try:
|
|
79
|
+
handler(msg)
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
yield msg
|
|
83
|
+
except Exception as exc:
|
|
84
|
+
closed_ok = websockets is not None and isinstance(
|
|
85
|
+
exc, getattr(websockets.exceptions, "ConnectionClosedOK", ())
|
|
86
|
+
)
|
|
87
|
+
if not closed_ok:
|
|
88
|
+
for handler in self._handlers["error"]:
|
|
89
|
+
try:
|
|
90
|
+
handler(exc)
|
|
91
|
+
except Exception:
|
|
92
|
+
pass
|
|
93
|
+
raise
|
|
94
|
+
finally:
|
|
95
|
+
self._capture_close()
|
|
96
|
+
if self.close_code is not None:
|
|
97
|
+
self._fire_close(self.close_code, self.close_reason or "")
|
|
98
|
+
|
|
99
|
+
def _fire_close(self, code: int, reason: str) -> None:
|
|
100
|
+
if self._close_fired:
|
|
101
|
+
return
|
|
102
|
+
self._close_fired = True
|
|
103
|
+
for handler in self._handlers["close"]:
|
|
104
|
+
try:
|
|
105
|
+
handler(code, reason)
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def _capture_close(self) -> None:
|
|
110
|
+
"""Read close code/reason from the underlying websockets.Connection once
|
|
111
|
+
the peer sends a Close frame. websockets exposes these as attributes on
|
|
112
|
+
the connection object after the receive loop sees Close."""
|
|
113
|
+
if self._conn is None:
|
|
114
|
+
return
|
|
115
|
+
code = getattr(self._conn, "close_code", None)
|
|
116
|
+
reason = getattr(self._conn, "close_reason", None)
|
|
117
|
+
if code is not None and self.close_code is None:
|
|
118
|
+
self.close_code = int(code)
|
|
119
|
+
if reason is not None and self.close_reason is None:
|
|
120
|
+
self.close_reason = str(reason)
|
|
121
|
+
|
|
122
|
+
def on(self, event: str, callback: Callable[..., Any]) -> None:
|
|
123
|
+
"""Register a callback for open, close, error, or message events."""
|
|
124
|
+
if event not in _VALID_EVENTS:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"event must be one of {sorted(_VALID_EVENTS)}, got {event!r}",
|
|
127
|
+
)
|
|
128
|
+
self._handlers[event].append(callback)
|
|
129
|
+
|
|
130
|
+
async def send(self, data: Any) -> None:
|
|
131
|
+
"""Send str/bytes as-is; anything else (dict, list, numbers) as JSON text."""
|
|
132
|
+
if not isinstance(data, (str, bytes, bytearray)):
|
|
133
|
+
data = json.dumps(data)
|
|
134
|
+
await self._conn.send(data)
|
|
135
|
+
|
|
136
|
+
async def close(self, code: int = 1000, reason: str = "") -> None:
|
|
137
|
+
if self._conn is not None:
|
|
138
|
+
await self._conn.close(code, reason)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
async def _connect(url: str, protocols: list[str], headers: dict[str, str]) -> Any:
|
|
142
|
+
"""websockets >= 14 takes ``additional_headers``; older releases take
|
|
143
|
+
``extra_headers``."""
|
|
144
|
+
assert websockets is not None
|
|
145
|
+
try:
|
|
146
|
+
return await websockets.connect(
|
|
147
|
+
url,
|
|
148
|
+
subprotocols=cast(Any, protocols or None),
|
|
149
|
+
additional_headers=headers or None,
|
|
150
|
+
)
|
|
151
|
+
except TypeError:
|
|
152
|
+
return await websockets.connect(
|
|
153
|
+
url,
|
|
154
|
+
subprotocols=cast(Any, protocols or None),
|
|
155
|
+
extra_headers=headers or None,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
__all__ = ["_TypedWebSocket"]
|
|
File without changes
|