···227227#[cfg(feature = "streaming")]
228228pub use stream::{ByteStream, ByteSink, StreamError, StreamErrorKind};
229229230230+#[cfg(feature = "streaming")]
231231+pub use xrpc::StreamingResponse;
232232+230233pub use types::value::*;
231234232235/// Authorization token types for XRPC requests.
+7
crates/jacquard-common/src/xrpc.rs
···99//! can inspect `error="invalid_token"` or `error="use_dpop_nonce"` and refresh/retry.
1010//! If the header is absent, parse the body and map auth errors to
1111//! `AuthError::TokenExpired`/`InvalidToken`.
1212+1313+#[cfg(feature = "streaming")]
1414+pub mod streaming;
1515+1616+#[cfg(feature = "streaming")]
1717+pub use streaming::StreamingResponse;
1818+1219use bytes::Bytes;
1320use http::{
1421 HeaderName, HeaderValue, Request, StatusCode,
+82
crates/jacquard-common/src/xrpc/streaming.rs
···11+//! Streaming support for XRPC requests and responses
22+33+use crate::stream::ByteStream;
44+use http::StatusCode;
55+66+/// XRPC streaming response
77+///
88+/// Similar to `Response<R>` but holds a streaming body instead of a buffer.
99+pub struct StreamingResponse {
1010+ parts: http::response::Parts,
1111+ body: ByteStream,
1212+}
1313+1414+impl StreamingResponse {
1515+ /// Create a new streaming response
1616+ pub fn new(parts: http::response::Parts, body: ByteStream) -> Self {
1717+ Self { parts, body }
1818+ }
1919+2020+ /// Get the HTTP status code
2121+ pub fn status(&self) -> StatusCode {
2222+ self.parts.status
2323+ }
2424+2525+ /// Get the response headers
2626+ pub fn headers(&self) -> &http::HeaderMap {
2727+ &self.parts.headers
2828+ }
2929+3030+ /// Get the response version
3131+ pub fn version(&self) -> http::Version {
3232+ self.parts.version
3333+ }
3434+3535+ /// Consume the response and return parts and body separately
3636+ pub fn into_parts(self) -> (http::response::Parts, ByteStream) {
3737+ (self.parts, self.body)
3838+ }
3939+4040+ /// Get mutable access to the body stream
4141+ pub fn body_mut(&mut self) -> &mut ByteStream {
4242+ &mut self.body
4343+ }
4444+4545+ /// Get a reference to the body stream
4646+ pub fn body(&self) -> &ByteStream {
4747+ &self.body
4848+ }
4949+}
5050+5151+impl std::fmt::Debug for StreamingResponse {
5252+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5353+ f.debug_struct("StreamingResponse")
5454+ .field("status", &self.parts.status)
5555+ .field("version", &self.parts.version)
5656+ .field("headers", &self.parts.headers)
5757+ .finish_non_exhaustive()
5858+ }
5959+}
6060+6161+#[cfg(test)]
6262+mod tests {
6363+ use super::*;
6464+ use bytes::Bytes;
6565+ use futures::stream;
6666+6767+ #[test]
6868+ fn streaming_response_holds_parts_and_body() {
6969+ // Build parts from a Response and extract them
7070+ let response = http::Response::builder()
7171+ .status(StatusCode::OK)
7272+ .body(())
7373+ .unwrap();
7474+ let (parts, _) = response.into_parts();
7575+7676+ let stream = stream::iter(vec![Ok(Bytes::from("test"))]);
7777+ let body = ByteStream::new(stream);
7878+7979+ let response = StreamingResponse::new(parts, body);
8080+ assert_eq!(response.status(), StatusCode::OK);
8181+ }
8282+}