+18
CHANGELOG.md
+18
CHANGELOG.md
···
1
1
# Changelog
2
2
3
+
4
+
## [0.5.4] - 2025-10-16
5
+
6
+
### Added
7
+
8
+
**Initial streaming client support** (`jacquard-common`)
9
+
- First primitives for streamed requests and responses
10
+
11
+
**`send_with_options()` method on XrpcClient** (`jacquard-common`, `jacquard-oauth`, `jacquard`)
12
+
- allows setting custom options per request in stateful client
13
+
- updated oauth and credential session clients to use it
14
+
- implementations should generally override provided auth with own internal auth
15
+
16
+
### Fixed
17
+
18
+
**`AgentSessionExt::upload_blob()` failed to authenticate** (`jacquard`)
19
+
- new `XrpcClient::send_with_options()` method now allows properly overriding the content-type header while still handling auth internally
20
+
3
21
## [0.5.3] - 2025-10-15
4
22
5
23
### Added
+14
-14
Cargo.lock
+14
-14
Cargo.lock
···
1850
1850
1851
1851
[[package]]
1852
1852
name = "jacquard"
1853
-
version = "0.5.3"
1853
+
version = "0.5.4"
1854
1854
dependencies = [
1855
1855
"bon",
1856
1856
"bytes",
···
1858
1858
"getrandom 0.2.16",
1859
1859
"http",
1860
1860
"jacquard-api 0.5.3",
1861
-
"jacquard-common 0.5.3",
1862
-
"jacquard-derive 0.5.3",
1861
+
"jacquard-common 0.5.4",
1862
+
"jacquard-derive 0.5.4",
1863
1863
"jacquard-identity 0.5.3",
1864
1864
"jacquard-oauth",
1865
1865
"jose-jwk",
···
1900
1900
dependencies = [
1901
1901
"bon",
1902
1902
"bytes",
1903
-
"jacquard-common 0.5.3",
1904
-
"jacquard-derive 0.5.3",
1903
+
"jacquard-common 0.5.4",
1904
+
"jacquard-derive 0.5.4",
1905
1905
"miette",
1906
1906
"serde",
1907
1907
"thiserror 2.0.17",
···
1918
1918
"bytes",
1919
1919
"chrono",
1920
1920
"jacquard",
1921
-
"jacquard-common 0.5.3",
1922
-
"jacquard-derive 0.5.3",
1921
+
"jacquard-common 0.5.4",
1922
+
"jacquard-derive 0.5.4",
1923
1923
"jacquard-identity 0.5.3",
1924
1924
"k256",
1925
1925
"miette",
···
1973
1973
1974
1974
[[package]]
1975
1975
name = "jacquard-common"
1976
-
version = "0.5.3"
1976
+
version = "0.5.4"
1977
1977
dependencies = [
1978
1978
"base64 0.22.1",
1979
1979
"bon",
···
2029
2029
2030
2030
[[package]]
2031
2031
name = "jacquard-derive"
2032
-
version = "0.5.3"
2032
+
version = "0.5.4"
2033
2033
dependencies = [
2034
-
"jacquard-common 0.5.3",
2034
+
"jacquard-common 0.5.4",
2035
2035
"proc-macro2",
2036
2036
"quote",
2037
2037
"serde",
···
2071
2071
"hickory-resolver",
2072
2072
"http",
2073
2073
"jacquard-api 0.5.3",
2074
-
"jacquard-common 0.5.3",
2074
+
"jacquard-common 0.5.4",
2075
2075
"miette",
2076
2076
"percent-encoding",
2077
2077
"reqwest",
···
2088
2088
2089
2089
[[package]]
2090
2090
name = "jacquard-lexicon"
2091
-
version = "0.5.3"
2091
+
version = "0.5.4"
2092
2092
dependencies = [
2093
2093
"async-trait",
2094
2094
"clap",
···
2116
2116
2117
2117
[[package]]
2118
2118
name = "jacquard-oauth"
2119
-
version = "0.5.3"
2119
+
version = "0.5.4"
2120
2120
dependencies = [
2121
2121
"base64 0.22.1",
2122
2122
"bytes",
···
2124
2124
"dashmap",
2125
2125
"elliptic-curve",
2126
2126
"http",
2127
-
"jacquard-common 0.5.3",
2127
+
"jacquard-common 0.5.4",
2128
2128
"jacquard-identity 0.5.3",
2129
2129
"jose-jwa",
2130
2130
"jose-jwk",
+1
-1
Cargo.toml
+1
-1
Cargo.toml
+3
-2
README.md
+3
-2
README.md
···
104
104
105
105
Highlights:
106
106
107
+
- initial streaming support
107
108
- experimental WASM support
108
109
- better value type deserialization helpers
109
110
- service auth implementation
···
124
125
```
125
126
126
127
127
-
### Streaming Support
128
+
### Initial Streaming Support
128
129
129
-
Jacquard supports efficient streaming for large payloads:
130
+
Jacquard is building out support for efficient streaming for large payloads:
130
131
131
132
- **Blob uploads/downloads**: Stream media without loading into memory
132
133
- **CAR file streaming**: Efficient repo sync operations
+1
-1
crates/jacquard-common/Cargo.toml
+1
-1
crates/jacquard-common/Cargo.toml
+23
crates/jacquard-common/src/xrpc.rs
+23
crates/jacquard-common/src/xrpc.rs
···
263
263
where
264
264
R: XrpcRequest + Send + Sync,
265
265
<R as XrpcRequest>::Response: Send + Sync;
266
+
267
+
/// Send an XRPC request and parse the response
268
+
#[cfg(not(target_arch = "wasm32"))]
269
+
fn send_with_opts<R>(
270
+
&self,
271
+
request: R,
272
+
opts: CallOptions<'_>,
273
+
) -> impl Future<Output = XrpcResult<Response<<R as XrpcRequest>::Response>>>
274
+
where
275
+
R: XrpcRequest + Send + Sync,
276
+
<R as XrpcRequest>::Response: Send + Sync,
277
+
Self: Sync;
278
+
279
+
/// Send an XRPC request and parse the response
280
+
#[cfg(target_arch = "wasm32")]
281
+
fn send_with_opts<R>(
282
+
&self,
283
+
request: R,
284
+
opts: CallOptions<'_>,
285
+
) -> impl Future<Output = XrpcResult<Response<<R as XrpcRequest>::Response>>>
286
+
where
287
+
R: XrpcRequest + Send + Sync,
288
+
<R as XrpcRequest>::Response: Send + Sync;
266
289
}
267
290
268
291
/// Stateless XRPC call builder.
+1
-1
crates/jacquard-oauth/Cargo.toml
+1
-1
crates/jacquard-oauth/Cargo.toml
+15
-2
crates/jacquard-oauth/src/client.rs
+15
-2
crates/jacquard-oauth/src/client.rs
···
436
436
437
437
async fn send<R>(&self, request: R) -> XrpcResult<Response<<R as XrpcRequest>::Response>>
438
438
where
439
-
R: XrpcRequest,
439
+
R: XrpcRequest + Send + Sync,
440
+
<R as XrpcRequest>::Response: Send + Sync,
441
+
{
442
+
let opts = self.options.read().await.clone();
443
+
self.send_with_opts(request, opts).await
444
+
}
445
+
446
+
async fn send_with_opts<R>(
447
+
&self,
448
+
request: R,
449
+
mut opts: CallOptions<'_>,
450
+
) -> XrpcResult<Response<<R as XrpcRequest>::Response>>
451
+
where
452
+
R: XrpcRequest + Send + Sync,
453
+
<R as XrpcRequest>::Response: Send + Sync,
440
454
{
441
455
let base_uri = self.base_uri();
442
-
let mut opts = self.options.read().await.clone();
443
456
opts.auth = Some(self.access_token().await);
444
457
let guard = self.data.read().await;
445
458
let mut dpop = guard.dpop_data.clone();
+14
-16
crates/jacquard/src/client.rs
+14
-16
crates/jacquard/src/client.rs
···
321
321
repo::{
322
322
create_record::CreateRecordOutput, delete_record::DeleteRecordOutput,
323
323
get_record::GetRecordResponse, put_record::PutRecordOutput,
324
-
upload_blob::UploadBlobResponse,
325
324
},
326
325
server::{create_session::CreateSessionOutput, refresh_session::RefreshSessionOutput},
327
326
};
···
738
737
let request = UploadBlob::new().body(bytes).build();
739
738
740
739
// Override Content-Type header with actual mime type instead of */*
741
-
let base = self.base_uri();
742
740
let mut opts = self.opts().await;
741
+
743
742
opts.extra_headers.push((
744
743
CONTENT_TYPE,
745
744
http::HeaderValue::from_str(mime_type.as_str()).map_err(|e| {
···
749
748
}
750
749
})?,
751
750
));
752
-
753
-
let response: Response<UploadBlobResponse> = {
754
-
let http_request =
755
-
xrpc::build_http_request(&base, &request, &opts).map_err(|e| {
756
-
AgentError::Client(ClientError::Transport(TransportError::from(e)))
757
-
})?;
758
-
759
-
let http_response = self.send_http(http_request).await.map_err(|e| {
760
-
AgentError::Client(ClientError::Transport(TransportError::Other(Box::new(e))))
761
-
})?;
762
-
763
-
xrpc::process_response(http_response)
764
-
}?;
765
-
751
+
let response = self.send_with_opts(request, opts).await?;
766
752
let output = response.into_output().map_err(|e| match e {
767
753
XrpcError::Auth(auth) => AgentError::Auth(auth),
768
754
XrpcError::Generic(g) => AgentError::Generic(g),
···
912
898
<R as XrpcRequest>::Response: Send + Sync,
913
899
{
914
900
async move { self.inner.send(request).await }
901
+
}
902
+
903
+
async fn send_with_opts<R>(
904
+
&self,
905
+
request: R,
906
+
opts: CallOptions<'_>,
907
+
) -> XrpcResult<Response<<R as XrpcRequest>::Response>>
908
+
where
909
+
R: XrpcRequest + Send + Sync,
910
+
<R as XrpcRequest>::Response: Send + Sync,
911
+
{
912
+
self.inner.send_with_opts(request, opts).await
915
913
}
916
914
}
917
915
+13
-1
crates/jacquard/src/client/credential_session.rs
+13
-1
crates/jacquard/src/client/credential_session.rs
···
431
431
R: XrpcRequest + Send + Sync,
432
432
<R as XrpcRequest>::Response: Send + Sync,
433
433
{
434
+
let opts = self.options.read().await.clone();
435
+
self.send_with_opts(request, opts).await
436
+
}
437
+
438
+
async fn send_with_opts<R>(
439
+
&self,
440
+
request: R,
441
+
mut opts: CallOptions<'_>,
442
+
) -> XrpcResult<Response<<R as XrpcRequest>::Response>>
443
+
where
444
+
R: XrpcRequest + Send + Sync,
445
+
<R as XrpcRequest>::Response: Send + Sync,
446
+
{
434
447
let base_uri = self.base_uri();
435
448
let auth = self.access_token().await;
436
-
let mut opts = self.options.read().await.clone();
437
449
opts.auth = auth;
438
450
let resp = self
439
451
.client