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
231            .sse_url
232            .clone()
233            .unwrap_or_else(|| format!("{}/stream/sse?session_id={}", base_url, self.session_id));
234
235        if let Some(ref token) = self.frame_token {
236            let sep = if url.contains('?') { "&" } else { "?" };
237            url = format!("{}{}token={}", url, sep, token);
238        }
239        if let Some(w) = width {
240            let sep = if url.contains('?') { "&" } else { "?" };
241            url = format!("{}{}width={}", url, sep, w);
242        }
243        if let Some(h) = height {
244            url = format!("{}&height={}", url, h);
245        }
246        url
247    }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
251pub struct ObserveOptions {
252    pub use_image: Option<bool>,
253    pub full_page: Option<bool>,
254    pub wait_ms: Option<u64>,
255    pub include_content: Option<bool>,
256}
257
258impl Default for ObserveOptions {
259    fn default() -> Self {
260        Self {
261            use_image: Some(true),
262            full_page: None,
263            wait_ms: None,
264            include_content: Some(true),
265        }
266    }
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
270pub struct RelayEvent {
271    pub ts: i64,
272    pub session_id: String,
273    pub category: String,
274    pub method: Option<String>,
275    pub level: Option<String>,
276    pub summary: Option<String>,
277    pub payload: Value,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
281pub struct RelayEventsResponse {
282    pub session_id: String,
283    pub count: usize,
284    pub events: Vec<RelayEvent>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
288pub struct RelaySessionInfo {
289    pub session_id: String,
290    pub connected: bool,
291    pub connected_at: i64,
292    #[serde(default)]
293    pub last_activity: Option<i64>,
294    #[serde(default)]
295    pub idle_secs: Option<i64>,
296    #[serde(default)]
297    pub user_email: Option<String>,
298    #[serde(default)]
299    pub tab_url: Option<String>,
300    #[serde(default)]
301    pub tab_title: Option<String>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
305pub struct RelaySessionListResponse {
306    pub sessions: Vec<RelaySessionInfo>,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
310#[serde(rename_all = "camelCase")]
311pub struct ShellCreateSessionRequest {
312    #[serde(default, skip_serializing_if = "Option::is_none")]
313    pub image: Option<String>,
314    #[serde(default, skip_serializing_if = "Option::is_none")]
315    pub language: Option<String>,
316    #[serde(default, skip_serializing_if = "Option::is_none")]
317    pub timeout_secs: Option<u32>,
318    #[serde(default, skip_serializing_if = "Option::is_none")]
319    pub working_dir: Option<String>,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
323#[serde(rename_all = "camelCase")]
324pub struct ShellCreateSessionResponse {
325    pub session_id: String,
326    pub status: String,
327    #[serde(default)]
328    pub worker_id: Option<String>,
329    #[serde(default)]
330    pub language: Option<String>,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
334#[serde(rename_all = "camelCase")]
335pub struct ShellSessionListItem {
336    pub session_id: String,
337    pub status: String,
338    pub image: String,
339    pub created_at: String,
340    pub last_activity: String,
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
344pub struct ShellSessionListResponse {
345    pub sessions: Vec<ShellSessionListItem>,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
349pub struct ShellTerminateResponse {
350    pub session_id: String,
351    pub status: String,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
355pub struct ShellExecRequest {
356    pub session_id: String,
357    pub command: String,
358    #[serde(default, skip_serializing_if = "Option::is_none")]
359    pub timeout_secs: Option<u32>,
360    #[serde(default, skip_serializing_if = "Option::is_none")]
361    pub working_dir: Option<String>,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
365pub struct ShellExecResult {
366    #[serde(default)]
367    pub stdout: String,
368    #[serde(default)]
369    pub stderr: String,
370    #[serde(default)]
371    pub exit_code: Option<i32>,
372    #[serde(default)]
373    pub duration_ms: Option<u64>,
374    #[serde(default)]
375    pub timed_out: bool,
376}
377
378#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
379pub struct ShellExecResponse {
380    pub session_id: String,
381    #[serde(flatten)]
382    pub result: ShellExecResult,
383}