feat(build): feature-sided builds — server/client sides independently selectable (ADR-039 Amendment 1)
Split the feature graph so consumers pulling only the import adapters (from_openapi / from_jsonschema / from_mcp) no longer compile the axum / hyper server stack, and server-only deployments no longer compile reqwest. One crate, one import path — sides cut by features, not by a crate split. Feature graph: - server (default): axum host, gateway, WS upgrade, to_openapi, to_mcp - client (default): client host, forward, from_jsonschema, from_openapi - openapi: shared OpenAPISpec model (implied by both sides) - mcp: from_mcp needs client, to_mcp needs server - wss: tungstenite transport (from_wss); tungstenite half of the shared WS↔byte-stream adapter - h2/http1: hyper protocol features; imply server Wire-contract neutral: gateway endpoints, ALPNs, and all public API shapes unchanged; defaults keep both sides on. Supporting changes: - forward.rs drops its axum::body::Bytes type leak (bytes crate types) - bounded_join + error-echo caps move to input_validation (usable by both sides; openapi_spec no longer imports from forward) - byte_adapter: axum flavor compiles under server, tungstenite under wss; the generic pumps stay shared (WS-11) - input_validation / openapi_spec import-only internals gated to the side that consumes them - http-body-util moves to dev-dependencies (was test-only) - integration-test required-features updated for the new sides - from_wss unit tests (axum producer harness) gated to server Verified: cargo test (defaults, 453) and --all-features (575) pass; lean side builds (client / server / client,mcp / client,wss / server,wss / openapi-only) build clean with zero warnings; clippy -D warnings clean across all feature combinations; fmt clean.
This commit is contained in:
+56
-21
@@ -14,39 +14,58 @@ exclude = [".opencode/", "AGENTS.md", "docs/", "tasks/", "Cargo.lock"]
|
||||
name = "alkhttp"
|
||||
|
||||
[features]
|
||||
default = ["h2", "http1"]
|
||||
default = ["server", "client", "h2", "http1"]
|
||||
# Producer side: the axum `Router` host (gateway routes, /healthz,
|
||||
# /openapi.json, /mcp, the WS upgrade path) and the hyper connection
|
||||
# driver. `openapi` rides along because /openapi.json is part of the
|
||||
# default surface (ADR-042/045); `http1` rides along so the hyper
|
||||
# auto-builder always has at least one protocol compiled.
|
||||
server = ["dep:axum", "dep:hyper", "dep:hyper-util", "dep:uuid", "openapi", "http1"]
|
||||
# Consumer side: the shared outbound client host and the HTTP-backed
|
||||
# import adapters (`from_jsonschema`, `from_openapi` with `openapi`,
|
||||
# `from_mcp` with `mcp`).
|
||||
client = ["dep:arc-swap", "dep:httpdate", "dep:percent-encoding", "dep:reqwest", "dep:reqwest-middleware", "dep:reqwest-retry", "dep:url"]
|
||||
# Shared OpenAPI document model: `from_openapi` parsing (client side)
|
||||
# and the `to_openapi` gateway projection (server side).
|
||||
openapi = ["dep:yaml_serde"]
|
||||
# MCP adapters: `from_mcp` needs `client`, `to_mcp` needs `server`.
|
||||
mcp = ["dep:rmcp"]
|
||||
# WebSocket transport (from_wss consumer; tungstenite flavor of the
|
||||
# WS ↔ byte-stream adapter).
|
||||
wss = ["dep:tokio-tungstenite"]
|
||||
test-support = ["dep:tokio-tungstenite"]
|
||||
h2 = ["dep:hyper", "hyper-util/http2", "hyper/http2"]
|
||||
http1 = ["dep:hyper", "hyper-util/http1", "hyper/http1"]
|
||||
# Test-only WS client helpers (downstream deployment tests); implies
|
||||
# `server` — the helpers live with the upgrade path.
|
||||
test-support = ["server", "dep:tokio-tungstenite"]
|
||||
# HTTP protocol features of the server connection driver (ADR-001).
|
||||
h2 = ["server", "dep:hyper", "hyper-util/http2", "hyper/http2"]
|
||||
http1 = ["server", "dep:hyper", "hyper-util/http1", "hyper/http1"]
|
||||
|
||||
[dependencies]
|
||||
alkcall = { version = "0.2", features = ["gateway"] }
|
||||
arc-swap = "1"
|
||||
axum = { version = "0.8", features = ["ws"] }
|
||||
arc-swap = { version = "1", optional = true }
|
||||
axum = { version = "0.8", optional = true, features = ["ws"] }
|
||||
bytes = "1"
|
||||
futures = "0.3"
|
||||
http = "1"
|
||||
httpdate = { version = "1", optional = true }
|
||||
hyper = { version = "1", optional = true, features = ["server"] }
|
||||
hyper-util = { version = "0.1", features = ["server", "service", "tokio"] }
|
||||
httpdate = "1"
|
||||
hyper-util = { version = "0.1", optional = true, features = ["server", "service", "tokio"] }
|
||||
jsonschema = { version = "0.46", default-features = false }
|
||||
reqwest = { version = "0.13", default-features = false, features = ["json", "stream", "rustls"] }
|
||||
reqwest-middleware = "0.5"
|
||||
reqwest-retry = "0.9"
|
||||
parking_lot = "0.12"
|
||||
percent-encoding = { version = "2", optional = true }
|
||||
reqwest = { version = "0.13", optional = true, default-features = false, features = ["json", "stream", "rustls"] }
|
||||
reqwest-middleware = { version = "0.5", optional = true }
|
||||
reqwest-retry = { version = "0.9", optional = true }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread", "io-util", "net", "fs", "time", "sync"] }
|
||||
tokio-tungstenite = { version = "0.29", optional = true, default-features = false, features = ["connect", "rustls-tls-webpki-roots", "handshake"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
yaml_serde = "0.10"
|
||||
async-trait = "0.1"
|
||||
tracing = "0.1"
|
||||
thiserror = "2"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
futures = "0.3"
|
||||
http = "1"
|
||||
http-body-util = "0.1"
|
||||
url = "2"
|
||||
percent-encoding = "2"
|
||||
parking_lot = "0.12"
|
||||
uuid = { version = "1", optional = true, features = ["v4"] }
|
||||
url = { version = "2", optional = true }
|
||||
yaml_serde = { version = "0.10", optional = true }
|
||||
rmcp = { version = "1.8", optional = true, default-features = false, features = [
|
||||
"client",
|
||||
"server",
|
||||
@@ -75,8 +94,24 @@ required-features = ["test-support"]
|
||||
|
||||
[[test]]
|
||||
name = "from_mcp_integration"
|
||||
required-features = ["mcp"]
|
||||
required-features = ["server", "client", "mcp"]
|
||||
|
||||
[[test]]
|
||||
name = "full_surface"
|
||||
required-features = ["mcp", "test-support"]
|
||||
required-features = ["server", "client", "openapi", "mcp", "test-support"]
|
||||
|
||||
[[test]]
|
||||
name = "client_config_reload"
|
||||
required-features = ["client"]
|
||||
|
||||
[[test]]
|
||||
name = "client_tls"
|
||||
required-features = ["client"]
|
||||
|
||||
[[test]]
|
||||
name = "retry_after_budget"
|
||||
required-features = ["client"]
|
||||
|
||||
[[test]]
|
||||
name = "retry_policy_wire"
|
||||
required-features = ["client"]
|
||||
Reference in New Issue
Block a user