convex-rust 0.0.0-stage → 0.1.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.
Files changed (90) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +137 -2
  3. package/dist/cli/bin.d.ts +2 -0
  4. package/dist/cli/bin.js +200 -0
  5. package/dist/cli/build.d.ts +146 -0
  6. package/dist/cli/build.js +902 -0
  7. package/dist/cli/demangle.d.ts +7 -0
  8. package/dist/cli/demangle.js +328 -0
  9. package/dist/cli/diagnostics.d.ts +34 -0
  10. package/dist/cli/diagnostics.js +296 -0
  11. package/dist/cli/generate.d.ts +29 -0
  12. package/dist/cli/generate.js +82 -0
  13. package/dist/cli/manifest.d.ts +24 -0
  14. package/dist/cli/manifest.js +75 -0
  15. package/dist/cli/rustApi.d.ts +78 -0
  16. package/dist/cli/rustApi.js +609 -0
  17. package/dist/cli/scaffold.d.ts +22 -0
  18. package/dist/cli/scaffold.js +114 -0
  19. package/dist/cli/shim.d.ts +30 -0
  20. package/dist/cli/shim.js +140 -0
  21. package/dist/cli/size.d.ts +17 -0
  22. package/dist/cli/size.js +73 -0
  23. package/dist/cli/split.d.ts +20 -0
  24. package/dist/cli/split.js +140 -0
  25. package/dist/cli/symbols.d.ts +30 -0
  26. package/dist/cli/symbols.js +133 -0
  27. package/dist/cli/toolchain.d.ts +53 -0
  28. package/dist/cli/toolchain.js +167 -0
  29. package/dist/cli/validators.d.ts +6 -0
  30. package/dist/cli/validators.js +66 -0
  31. package/dist/cli/wasisdk.d.ts +82 -0
  32. package/dist/cli/wasisdk.js +220 -0
  33. package/dist/cli/wasmHints.d.ts +29 -0
  34. package/dist/cli/wasmHints.js +172 -0
  35. package/dist/runtime/base64.d.ts +4 -0
  36. package/dist/runtime/base64.js +77 -0
  37. package/dist/runtime/crypto.d.ts +7 -0
  38. package/dist/runtime/crypto.js +421 -0
  39. package/dist/runtime/fastjson.d.ts +3 -0
  40. package/dist/runtime/fastjson.js +170 -0
  41. package/dist/runtime/host.d.ts +73 -0
  42. package/dist/runtime/host.js +1230 -0
  43. package/dist/runtime/http.d.ts +40 -0
  44. package/dist/runtime/http.js +157 -0
  45. package/dist/runtime/index.d.ts +2 -0
  46. package/dist/runtime/index.js +1 -0
  47. package/dist/runtime/intl.d.ts +5 -0
  48. package/dist/runtime/intl.js +69 -0
  49. package/dist/runtime/tmpfs.d.ts +129 -0
  50. package/dist/runtime/tmpfs.js +624 -0
  51. package/dist/runtime/wasi.d.ts +33 -0
  52. package/dist/runtime/wasi.js +216 -0
  53. package/package.json +57 -4
  54. package/rust/convex_fn/Cargo.toml +32 -0
  55. package/rust/convex_fn/LICENSE +201 -0
  56. package/rust/convex_fn/build.rs +3 -0
  57. package/rust/convex_fn/src/args.rs +416 -0
  58. package/rust/convex_fn/src/crypto.rs +237 -0
  59. package/rust/convex_fn/src/ctx.rs +1044 -0
  60. package/rust/convex_fn/src/dns.rs +356 -0
  61. package/rust/convex_fn/src/error.rs +179 -0
  62. package/rust/convex_fn/src/executor.rs +577 -0
  63. package/rust/convex_fn/src/fanout.rs +106 -0
  64. package/rust/convex_fn/src/fs.rs +19 -0
  65. package/rust/convex_fn/src/function_ref.rs +219 -0
  66. package/rust/convex_fn/src/http.rs +441 -0
  67. package/rust/convex_fn/src/intl.rs +75 -0
  68. package/rust/convex_fn/src/lib.rs +242 -0
  69. package/rust/convex_fn/src/logging.rs +222 -0
  70. package/rust/convex_fn/src/native.rs +360 -0
  71. package/rust/convex_fn/src/net.rs +122 -0
  72. package/rust/convex_fn/src/sys.rs +178 -0
  73. package/rust/convex_fn/src/values.rs +121 -0
  74. package/rust/convex_fn/src/wire.rs +579 -0
  75. package/rust/convex_fn_macros/Cargo.toml +13 -0
  76. package/rust/convex_fn_macros/LICENSE +201 -0
  77. package/rust/convex_fn_macros/src/derive.rs +632 -0
  78. package/rust/convex_fn_macros/src/lib.rs +338 -0
  79. package/rust/convex_reqwest/Cargo.toml +69 -0
  80. package/rust/convex_reqwest/LICENSE +201 -0
  81. package/rust/convex_reqwest/src/blocking.rs +287 -0
  82. package/rust/convex_reqwest/src/body.rs +152 -0
  83. package/rust/convex_reqwest/src/client.rs +359 -0
  84. package/rust/convex_reqwest/src/config_types.rs +150 -0
  85. package/rust/convex_reqwest/src/error.rs +241 -0
  86. package/rust/convex_reqwest/src/into_url.rs +57 -0
  87. package/rust/convex_reqwest/src/lib.rs +42 -0
  88. package/rust/convex_reqwest/src/multipart.rs +140 -0
  89. package/rust/convex_reqwest/src/request.rs +358 -0
  90. package/rust/convex_reqwest/src/response.rs +310 -0
@@ -0,0 +1,241 @@
1
+ use std::{error::Error as StdError, fmt};
2
+
3
+ use http::StatusCode;
4
+ use url::Url;
5
+
6
+ pub type BoxError = Box<dyn StdError + Send + Sync>;
7
+
8
+ /// `Result<T, reqwest::Error>`.
9
+ pub type Result<T> = std::result::Result<T, Error>;
10
+
11
+ /// The errors that may occur when processing a `Request`.
12
+ pub struct Error {
13
+ inner: Box<Inner>,
14
+ }
15
+
16
+ struct Inner {
17
+ kind: Kind,
18
+ source: Option<BoxError>,
19
+ url: Option<Url>,
20
+ }
21
+
22
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
23
+ pub(crate) enum Kind {
24
+ Builder,
25
+ Request,
26
+ #[allow(dead_code)]
27
+ Redirect,
28
+ Status(StatusCode),
29
+ Body,
30
+ Decode,
31
+ }
32
+
33
+ /// Marker source for deadline expiry; `Error::is_timeout` looks for it.
34
+ #[derive(Debug)]
35
+ pub(crate) struct TimedOut;
36
+
37
+ impl fmt::Display for TimedOut {
38
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39
+ f.write_str("operation timed out")
40
+ }
41
+ }
42
+ impl StdError for TimedOut {}
43
+
44
+ /// Marker source for a failed host fetch (the closest thing to a connect error).
45
+ #[derive(Debug)]
46
+ pub(crate) struct FetchFailed(pub(crate) String);
47
+
48
+ impl fmt::Display for FetchFailed {
49
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50
+ f.write_str(&self.0)
51
+ }
52
+ }
53
+ impl StdError for FetchFailed {}
54
+
55
+ #[derive(Debug)]
56
+ struct Message(String);
57
+ impl fmt::Display for Message {
58
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59
+ f.write_str(&self.0)
60
+ }
61
+ }
62
+ impl StdError for Message {}
63
+
64
+ impl Error {
65
+ pub(crate) fn new<E: Into<BoxError>>(kind: Kind, source: Option<E>) -> Error {
66
+ Error {
67
+ inner: Box::new(Inner {
68
+ kind,
69
+ source: source.map(Into::into),
70
+ url: None,
71
+ }),
72
+ }
73
+ }
74
+
75
+ pub(crate) fn builder<E: Into<BoxError>>(e: E) -> Error {
76
+ Error::new(Kind::Builder, Some(e))
77
+ }
78
+
79
+ pub(crate) fn builder_msg(m: impl Into<String>) -> Error {
80
+ Error::builder(Message(m.into()))
81
+ }
82
+
83
+ pub(crate) fn request<E: Into<BoxError>>(e: E) -> Error {
84
+ Error::new(Kind::Request, Some(e))
85
+ }
86
+
87
+ pub(crate) fn body<E: Into<BoxError>>(e: E) -> Error {
88
+ Error::new(Kind::Body, Some(e))
89
+ }
90
+
91
+ pub(crate) fn decode<E: Into<BoxError>>(e: E) -> Error {
92
+ Error::new(Kind::Decode, Some(e))
93
+ }
94
+
95
+ pub(crate) fn timeout(kind: Kind) -> Error {
96
+ Error::new(kind, Some(TimedOut))
97
+ }
98
+
99
+ pub(crate) fn status_code(url: Url, status: StatusCode) -> Error {
100
+ Error::new(Kind::Status(status), None::<BoxError>).with_url(url)
101
+ }
102
+
103
+ /// Returns a possible URL related to this error.
104
+ pub fn url(&self) -> Option<&Url> {
105
+ self.inner.url.as_ref()
106
+ }
107
+
108
+ /// Returns a mutable reference to the URL related to this error.
109
+ pub fn url_mut(&mut self) -> Option<&mut Url> {
110
+ self.inner.url.as_mut()
111
+ }
112
+
113
+ /// Adds a url related to this error (overwriting any existing).
114
+ pub fn with_url(mut self, url: Url) -> Self {
115
+ self.inner.url = Some(url);
116
+ self
117
+ }
118
+
119
+ /// Strips the related url from this error (if, for example, it contains
120
+ /// sensitive information).
121
+ pub fn without_url(mut self) -> Self {
122
+ self.inner.url = None;
123
+ self
124
+ }
125
+
126
+ /// Returns true if the error is from a type `Builder`.
127
+ pub fn is_builder(&self) -> bool {
128
+ matches!(self.inner.kind, Kind::Builder)
129
+ }
130
+
131
+ /// Returns true if the error is from a `RedirectPolicy`.
132
+ pub fn is_redirect(&self) -> bool {
133
+ matches!(self.inner.kind, Kind::Redirect)
134
+ }
135
+
136
+ /// Returns true if the error is from `Response::error_for_status`.
137
+ pub fn is_status(&self) -> bool {
138
+ matches!(self.inner.kind, Kind::Status(_))
139
+ }
140
+
141
+ /// Returns true if the error is related to a timeout.
142
+ pub fn is_timeout(&self) -> bool {
143
+ let mut source = self.inner.source.as_deref().map(|e| e as &dyn StdError);
144
+ while let Some(err) = source {
145
+ if err.is::<TimedOut>() {
146
+ return true;
147
+ }
148
+ source = err.source();
149
+ }
150
+ false
151
+ }
152
+
153
+ /// Returns true if the error is related to the request.
154
+ pub fn is_request(&self) -> bool {
155
+ matches!(self.inner.kind, Kind::Request)
156
+ }
157
+
158
+ /// Returns true if the error is related to connect (here: the host's
159
+ /// fetch task failed before a response head arrived).
160
+ pub fn is_connect(&self) -> bool {
161
+ let mut source = self.inner.source.as_deref().map(|e| e as &dyn StdError);
162
+ while let Some(err) = source {
163
+ if err.is::<FetchFailed>() {
164
+ return true;
165
+ }
166
+ source = err.source();
167
+ }
168
+ false
169
+ }
170
+
171
+ /// Returns true if the error is related to the request or response body.
172
+ pub fn is_body(&self) -> bool {
173
+ matches!(self.inner.kind, Kind::Body)
174
+ }
175
+
176
+ /// Returns true if the error is related to decoding the response's body.
177
+ pub fn is_decode(&self) -> bool {
178
+ matches!(self.inner.kind, Kind::Decode)
179
+ }
180
+
181
+ /// Returns the status code, if the error was generated from a response.
182
+ pub fn status(&self) -> Option<StatusCode> {
183
+ match self.inner.kind {
184
+ Kind::Status(code) => Some(code),
185
+ _ => None,
186
+ }
187
+ }
188
+ }
189
+
190
+ impl fmt::Debug for Error {
191
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192
+ let mut b = f.debug_struct("reqwest::Error");
193
+ b.field("kind", &self.inner.kind);
194
+ if let Some(url) = &self.inner.url {
195
+ b.field("url", &url.as_str());
196
+ }
197
+ if let Some(source) = &self.inner.source {
198
+ b.field("source", source);
199
+ }
200
+ b.finish()
201
+ }
202
+ }
203
+
204
+ impl fmt::Display for Error {
205
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206
+ match self.inner.kind {
207
+ Kind::Builder => f.write_str("builder error")?,
208
+ Kind::Request => f.write_str("error sending request")?,
209
+ Kind::Body => f.write_str("request or response body error")?,
210
+ Kind::Decode => f.write_str("error decoding response body")?,
211
+ Kind::Redirect => f.write_str("error following redirect")?,
212
+ Kind::Status(code) => {
213
+ let prefix = if code.is_client_error() {
214
+ "HTTP status client error"
215
+ } else {
216
+ "HTTP status server error"
217
+ };
218
+ write!(f, "{prefix} ({code})")?;
219
+ }
220
+ }
221
+ if let Some(url) = &self.inner.url {
222
+ write!(f, " for url ({url})")?;
223
+ }
224
+ if let Some(e) = &self.inner.source {
225
+ write!(f, ": {e}")?;
226
+ }
227
+ Ok(())
228
+ }
229
+ }
230
+
231
+ impl StdError for Error {
232
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
233
+ self.inner.source.as_ref().map(|e| &**e as _)
234
+ }
235
+ }
236
+
237
+ impl From<Error> for std::io::Error {
238
+ fn from(e: Error) -> Self {
239
+ std::io::Error::other(e)
240
+ }
241
+ }
@@ -0,0 +1,57 @@
1
+ use url::Url;
2
+
3
+ use crate::error::{Error, Result};
4
+
5
+ /// A trait to try to convert some type into a `Url`.
6
+ pub trait IntoUrl: IntoUrlSealed {}
7
+
8
+ impl IntoUrl for Url {}
9
+ impl IntoUrl for String {}
10
+ impl IntoUrl for &str {}
11
+ impl IntoUrl for &String {}
12
+
13
+ #[doc(hidden)]
14
+ pub trait IntoUrlSealed {
15
+ fn into_url(self) -> Result<Url>;
16
+ fn as_str(&self) -> &str;
17
+ }
18
+
19
+ impl IntoUrlSealed for Url {
20
+ fn into_url(self) -> Result<Url> {
21
+ if self.has_host() {
22
+ Ok(self)
23
+ } else {
24
+ Err(Error::builder_msg("URL scheme is not allowed").with_url(self))
25
+ }
26
+ }
27
+ fn as_str(&self) -> &str {
28
+ self.as_ref()
29
+ }
30
+ }
31
+
32
+ impl IntoUrlSealed for &str {
33
+ fn into_url(self) -> Result<Url> {
34
+ Url::parse(self).map_err(Error::builder)?.into_url()
35
+ }
36
+ fn as_str(&self) -> &str {
37
+ self
38
+ }
39
+ }
40
+
41
+ impl IntoUrlSealed for &String {
42
+ fn into_url(self) -> Result<Url> {
43
+ (&**self).into_url()
44
+ }
45
+ fn as_str(&self) -> &str {
46
+ self.as_ref()
47
+ }
48
+ }
49
+
50
+ impl IntoUrlSealed for String {
51
+ fn into_url(self) -> Result<Url> {
52
+ (&*self).into_url()
53
+ }
54
+ fn as_str(&self) -> &str {
55
+ self.as_ref()
56
+ }
57
+ }
@@ -0,0 +1,42 @@
1
+ //! `reqwest`-compatible HTTP client for `"use rust"` actions (DX-06, D-010).
2
+ //!
3
+ //! This crate is named `reqwest` and is swapped in with
4
+ //! `[patch.crates-io] reqwest = { path = ... }` (the Convex CLI writes that
5
+ //! into `convex/rust/.cargo/config.toml`). Vendor crates built on reqwest then
6
+ //! send their requests through `convex_fn`'s `Fetch` task, so they keep the
7
+ //! host's egress policy, limits, metering and logs.
8
+ //!
9
+ //! Notes on fidelity:
10
+ //! - Needs the SDK's tokio runtime (`convex_fn` feature `tokio-rt`, enabled by
11
+ //! this crate): timeouts use `tokio::time`.
12
+ //! - TLS, proxies, pooling, compression, cookies and redirects are the host's;
13
+ //! the builder options for them are accepted and ignored.
14
+ //! - Response bodies arrive in the chunks the host delivers.
15
+ //! - Only UTF-8 text decoding is supported.
16
+
17
+ pub mod blocking;
18
+ mod body;
19
+ mod client;
20
+ mod config_types;
21
+ mod error;
22
+ mod into_url;
23
+ pub mod multipart;
24
+ mod request;
25
+ mod response;
26
+
27
+ pub use bytes::Bytes;
28
+ pub use http::{header, Method, StatusCode, Version};
29
+ pub use url::Url;
30
+
31
+ pub use self::body::Body;
32
+ pub use self::client::{Client, ClientBuilder};
33
+ pub use self::config_types::{redirect, tls, Proxy};
34
+ pub use self::error::{Error, Result};
35
+ pub use self::into_url::IntoUrl;
36
+ pub use self::request::{Request, RequestBuilder};
37
+ pub use self::response::{Response, ResponseBuilderExt};
38
+
39
+ /// Shortcut method to quickly make a `GET` request.
40
+ pub async fn get<T: IntoUrl>(url: T) -> Result<Response> {
41
+ Client::builder().build()?.get(url).send().await
42
+ }
@@ -0,0 +1,140 @@
1
+ //! `multipart/form-data` bodies, buffered (the host fetch task takes one body).
2
+
3
+ use std::{
4
+ borrow::Cow,
5
+ sync::atomic::{AtomicU64, Ordering},
6
+ };
7
+
8
+ use http::header::HeaderMap;
9
+
10
+ use crate::error::{Error, Result};
11
+
12
+ /// A multipart/form-data request body.
13
+ #[derive(Debug)]
14
+ pub struct Form {
15
+ boundary: String,
16
+ parts: Vec<(Cow<'static, str>, Part)>,
17
+ }
18
+
19
+ /// A field in a multipart form.
20
+ #[derive(Debug)]
21
+ pub struct Part {
22
+ data: Vec<u8>,
23
+ mime: Option<String>,
24
+ file_name: Option<Cow<'static, str>>,
25
+ headers: HeaderMap,
26
+ }
27
+
28
+ impl Default for Form {
29
+ fn default() -> Self {
30
+ Self::new()
31
+ }
32
+ }
33
+
34
+ impl Form {
35
+ pub fn new() -> Form {
36
+ static COUNTER: AtomicU64 = AtomicU64::new(0);
37
+ let nanos = std::time::SystemTime::now()
38
+ .duration_since(std::time::UNIX_EPOCH)
39
+ .map_or(0, |d| d.as_nanos() as u64);
40
+ let n = COUNTER.fetch_add(1, Ordering::Relaxed);
41
+ Form {
42
+ boundary: format!("{nanos:016x}{n:016x}"),
43
+ parts: vec![],
44
+ }
45
+ }
46
+
47
+ pub fn boundary(&self) -> &str {
48
+ &self.boundary
49
+ }
50
+
51
+ pub fn text<T, U>(self, name: T, value: U) -> Form
52
+ where
53
+ T: Into<Cow<'static, str>>,
54
+ U: Into<Cow<'static, str>>,
55
+ {
56
+ self.part(name, Part::text(value))
57
+ }
58
+
59
+ pub fn part<T: Into<Cow<'static, str>>>(mut self, name: T, part: Part) -> Form {
60
+ self.parts.push((name.into(), part));
61
+ self
62
+ }
63
+
64
+ pub(crate) fn into_bytes(self) -> Vec<u8> {
65
+ let mut out = Vec::new();
66
+ for (name, part) in &self.parts {
67
+ out.extend_from_slice(format!("--{}\r\n", self.boundary).as_bytes());
68
+ let mut disp = format!("Content-Disposition: form-data; name=\"{}\"", escape(name));
69
+ if let Some(f) = &part.file_name {
70
+ disp.push_str(&format!("; filename=\"{}\"", escape(f)));
71
+ }
72
+ out.extend_from_slice(disp.as_bytes());
73
+ out.extend_from_slice(b"\r\n");
74
+ if let Some(m) = &part.mime {
75
+ out.extend_from_slice(format!("Content-Type: {m}\r\n").as_bytes());
76
+ }
77
+ for (k, v) in &part.headers {
78
+ out.extend_from_slice(k.as_str().as_bytes());
79
+ out.extend_from_slice(b": ");
80
+ out.extend_from_slice(v.as_bytes());
81
+ out.extend_from_slice(b"\r\n");
82
+ }
83
+ out.extend_from_slice(b"\r\n");
84
+ out.extend_from_slice(&part.data);
85
+ out.extend_from_slice(b"\r\n");
86
+ }
87
+ out.extend_from_slice(format!("--{}--\r\n", self.boundary).as_bytes());
88
+ out
89
+ }
90
+ }
91
+
92
+ fn escape(s: &str) -> String {
93
+ s.replace('\\', "\\\\")
94
+ .replace('"', "\\\"")
95
+ .replace('\r', "%0D")
96
+ .replace('\n', "%0A")
97
+ }
98
+
99
+ impl Part {
100
+ pub fn text<T: Into<Cow<'static, str>>>(value: T) -> Part {
101
+ Part::bytes(value.into().into_owned().into_bytes())
102
+ }
103
+
104
+ pub fn bytes<T: Into<Cow<'static, [u8]>>>(value: T) -> Part {
105
+ Part {
106
+ data: value.into().into_owned(),
107
+ mime: None,
108
+ file_name: None,
109
+ headers: HeaderMap::new(),
110
+ }
111
+ }
112
+
113
+ pub fn file_name<T: Into<Cow<'static, str>>>(mut self, filename: T) -> Part {
114
+ self.file_name = Some(filename.into());
115
+ self
116
+ }
117
+
118
+ pub fn mime_str(mut self, mime: &str) -> Result<Part> {
119
+ let m: mime_check::Mime = mime_check::parse(mime)?;
120
+ self.mime = Some(m);
121
+ Ok(self)
122
+ }
123
+
124
+ pub fn headers(mut self, headers: HeaderMap) -> Part {
125
+ self.headers = headers;
126
+ self
127
+ }
128
+ }
129
+
130
+ mod mime_check {
131
+ use super::*;
132
+ pub type Mime = String;
133
+ pub fn parse(s: &str) -> Result<Mime> {
134
+ if s.contains('/') && !s.contains(['\r', '\n']) {
135
+ Ok(s.to_string())
136
+ } else {
137
+ Err(Error::builder_msg(format!("invalid mime type: {s}")))
138
+ }
139
+ }
140
+ }