Skip to main content

browsr_types/
client_api.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
6#[serde(rename_all = "camelCase")]
7pub enum ScrapeFormat {
8    Markdown,
9    Summary,
10    Html,
11    RawHtml,
12    Screenshot,
13    Links,
14    Json,
15    Images,
16    Branding,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
20#[serde(rename_all = "camelCase")]
21pub struct JsonExtractionOptions {
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub prompt: Option<String>,
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub schema: Option<Value>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
29#[serde(rename_all = "camelCase")]
30pub struct ScrapeAction {
31    #[serde(rename = "type")]
32    pub action_type: String,
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub selector: Option<String>,
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    pub text: Option<String>,
37    #[serde(default, skip_serializing_if = "Option::is_none")]
38    pub milliseconds: Option<u64>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub expression: Option<String>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
44#[serde(rename_all = "camelCase")]
45pub struct ScrapeApiRequest {
46    pub url: String,
47    #[serde(default = "default_scrape_formats")]
48    pub formats: Vec<ScrapeFormat>,
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub wait_for: Option<u64>,
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub actions: Option<Vec<ScrapeAction>>,
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub json_options: Option<JsonExtractionOptions>,
55    #[serde(default = "default_true")]
56    pub only_main_content: bool,
57    #[serde(default = "default_true")]
58    pub remove_base64_images: bool,
59}
60
61fn default_scrape_formats() -> Vec<ScrapeFormat> {
62    vec![ScrapeFormat::Markdown]
63}
64
65fn default_true() -> bool {
66    true
67}
68
69impl ScrapeApiRequest {
70    pub fn new(url: impl Into<String>) -> Self {
71        Self {
72            url: url.into(),
73            formats: vec![ScrapeFormat::Markdown],
74            wait_for: None,
75            actions: None,
76            json_options: None,
77            only_main_content: true,
78            remove_base64_images: true,
79        }
80    }
81
82    pub fn with_formats(mut self, formats: Vec<ScrapeFormat>) -> Self {
83        self.formats = formats;
84        self
85    }
86
87    pub fn with_wait(mut self, ms: u64) -> Self {
88        self.wait_for = Some(ms);
89        self
90    }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
94#[serde(rename_all = "camelCase")]
95pub struct PageMetadata {
96    pub title: Option<String>,
97    pub description: Option<String>,
98    #[serde(rename = "sourceURL")]
99    pub source_url: String,
100    pub status_code: Option<u16>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
104pub struct ExtractedLink {
105    pub href: String,
106    #[serde(default)]
107    pub text: String,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
111pub struct ExtractedImage {
112    pub src: String,
113    #[serde(default)]
114    pub alt: Option<String>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
118pub struct BrandingInfo {
119    #[serde(default)]
120    pub colors: Option<Vec<String>>,
121    #[serde(default)]
122    pub fonts: Option<Vec<String>>,
123    #[serde(default)]
124    pub logo: Option<String>,
125    #[serde(default)]
126    pub favicon: Option<String>,
127    #[serde(default)]
128    pub name: Option<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
132#[serde(rename_all = "camelCase")]
133pub struct ScrapeData {
134    #[serde(default)]
135    pub markdown: Option<String>,
136    #[serde(default)]
137    pub summary: Option<String>,
138    #[serde(default)]
139    pub html: Option<String>,
140    #[serde(default)]
141    pub raw_html: Option<String>,
142    #[serde(default)]
143    pub screenshot: Option<String>,
144    #[serde(default)]
145    pub links: Option<Vec<ExtractedLink>>,
146    #[serde(default)]
147    pub json: Option<Value>,
148    #[serde(default)]
149    pub images: Option<Vec<ExtractedImage>>,
150    #[serde(default)]
151    pub branding: Option<BrandingInfo>,
152    pub metadata: PageMetadata,
153    #[serde(default)]
154    pub warning: Option<String>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
158pub struct ScrapeApiResponse {
159    pub success: bool,
160    pub data: ScrapeData,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
164#[serde(rename_all = "camelCase")]
165pub struct CrawlApiRequest {
166    pub url: String,
167    #[serde(default = "default_crawl_limit")]
168    pub limit: usize,
169    #[serde(default = "default_crawl_depth")]
170    pub max_depth: usize,
171    #[serde(default = "default_scrape_formats")]
172    pub formats: Vec<ScrapeFormat>,
173    #[serde(default, skip_serializing_if = "Option::is_none")]
174    pub wait_for: Option<u64>,
175    #[serde(default, skip_serializing_if = "Option::is_none")]
176    pub include_paths: Option<Vec<String>>,
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub exclude_paths: Option<Vec<String>>,
179    #[serde(default = "default_true")]
180    pub only_main_content: bool,
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub json_options: Option<JsonExtractionOptions>,
183}
184
185fn default_crawl_limit() -> usize {
186    10
187}
188
189fn default_crawl_depth() -> usize {
190    2
191}
192
193impl CrawlApiRequest {
194    pub fn new(url: impl Into<String>) -> Self {
195        Self {
196            url: url.into(),
197            limit: 10,
198            max_depth: 2,
199            formats: vec![ScrapeFormat::Markdown],
200            wait_for: None,
201            include_paths: None,
202            exclude_paths: None,
203            only_main_content: true,
204            json_options: None,
205        }
206    }
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
210pub struct CrawlApiResponse {
211    pub success: bool,
212    pub total: usize,
213    pub completed: usize,
214    pub data: Vec<ScrapeData>,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
218pub struct SessionCreated {
219    pub session_id: String,
220    #[serde(default)]
221    pub sse_url: Option<String>,
222    #[serde(default)]
223    pub frame_url: Option<String>,
224    #[serde(default)]
225    pub frame_token: Option<String>,
226}
227
228impl SessionCreated {
229    pub fn build_sse_url(&self, base_url: &str, width: Option<u32>, height: Option<u32>) -> String {
230        let mut url = self.sse_url.clone().unwrap_or_else(|| {
231            format!("{}/stream/sse?session_id={}", base_url, self.session_id)
232        });
233
234        if let Some(ref token) = self.frame_token {
235            let sep = if url.contains('?') { "&" } else { "?" };
236            url = format!("{}{}token={}", url, sep, token);
237        }
238        if let Some(w) = width {
239            let sep = if url.contains('?') { "&" } else { "?" };
240            url = format!("{}{}width={}", url, sep, w);
241        }
242        if let Some(h) = height {
243            url = format!("{}&height={}", url, h);
244        }
245        url
246    }
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
250pub struct ObserveOptions {
251    pub use_image: Option<bool>,
252    pub full_page: Option<bool>,
253    pub wait_ms: Option<u64>,
254    pub include_content: Option<bool>,
255}
256
257impl Default for ObserveOptions {
258    fn default() -> Self {
259        Self {
260            use_image: Some(true),
261            full_page: None,
262            wait_ms: None,
263            include_content: Some(true),
264        }
265    }
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
269pub struct RelayEvent {
270    pub ts: i64,
271    pub session_id: String,
272    pub category: String,
273    pub method: Option<String>,
274    pub level: Option<String>,
275    pub summary: Option<String>,
276    pub payload: Value,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
280pub struct RelayEventsResponse {
281    pub session_id: String,
282    pub count: usize,
283    pub events: Vec<RelayEvent>,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
287pub struct RelaySessionInfo {
288    pub session_id: String,
289    pub connected: bool,
290    pub connected_at: i64,
291    #[serde(default)]
292    pub last_activity: Option<i64>,
293    #[serde(default)]
294    pub idle_secs: Option<i64>,
295    #[serde(default)]
296    pub user_email: Option<String>,
297    #[serde(default)]
298    pub tab_url: Option<String>,
299    #[serde(default)]
300    pub tab_title: Option<String>,
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
304pub struct RelaySessionListResponse {
305    pub sessions: Vec<RelaySessionInfo>,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
309#[serde(rename_all = "camelCase")]
310pub struct ShellCreateSessionRequest {
311    #[serde(default, skip_serializing_if = "Option::is_none")]
312    pub image: Option<String>,
313    #[serde(default, skip_serializing_if = "Option::is_none")]
314    pub language: Option<String>,
315    #[serde(default, skip_serializing_if = "Option::is_none")]
316    pub timeout_secs: Option<u32>,
317    #[serde(default, skip_serializing_if = "Option::is_none")]
318    pub working_dir: Option<String>,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
322#[serde(rename_all = "camelCase")]
323pub struct ShellCreateSessionResponse {
324    pub session_id: String,
325    pub status: String,
326    #[serde(default)]
327    pub worker_id: Option<String>,
328    #[serde(default)]
329    pub language: Option<String>,
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
333#[serde(rename_all = "camelCase")]
334pub struct ShellSessionListItem {
335    pub session_id: String,
336    pub status: String,
337    pub image: String,
338    pub created_at: String,
339    pub last_activity: String,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
343pub struct ShellSessionListResponse {
344    pub sessions: Vec<ShellSessionListItem>,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
348pub struct ShellTerminateResponse {
349    pub session_id: String,
350    pub status: String,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
354pub struct ShellExecRequest {
355    pub session_id: String,
356    pub command: String,
357    #[serde(default, skip_serializing_if = "Option::is_none")]
358    pub timeout_secs: Option<u32>,
359    #[serde(default, skip_serializing_if = "Option::is_none")]
360    pub working_dir: Option<String>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
364pub struct ShellExecResult {
365    #[serde(default)]
366    pub stdout: String,
367    #[serde(default)]
368    pub stderr: String,
369    #[serde(default)]
370    pub exit_code: Option<i32>,
371    #[serde(default)]
372    pub duration_ms: Option<u64>,
373    #[serde(default)]
374    pub timed_out: bool,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
378pub struct ShellExecResponse {
379    pub session_id: String,
380    #[serde(flatten)]
381    pub result: ShellExecResult,
382}