crawlberg 1.1.4 → 1.3.3

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.
@@ -1,5 +1,5 @@
1
1
  // This file is auto-generated by alef. DO NOT EDIT.
2
- // alef:hash:899a9b0b3964b8cc10628ac686b74140e934aa910533d6b9333c2acf97f2773b
2
+ // alef:hash:9312cb415995255492557432651036cfe6b0edf4ccd0b27a88d0cb3c5a7d1466
3
3
  // Re-generate with: alef generate
4
4
  #![allow(dead_code, unused_imports, unused_variables)]
5
5
  #![allow(
@@ -316,7 +316,7 @@ impl ContentConfig {
316
316
  exclude_selectors: kwargs
317
317
  .get(ruby.to_symbol("exclude_selectors"))
318
318
  .and_then(|v| <Vec<String>>::try_convert(v).ok())
319
- .unwrap_or_default(),
319
+ .unwrap_or(vec!["noscript".to_string()]),
320
320
  skip_images: kwargs
321
321
  .get(ruby.to_symbol("skip_images"))
322
322
  .and_then(|v| bool::try_convert(v).ok())
@@ -467,7 +467,7 @@ impl BrowserConfig {
467
467
  wait: kwargs
468
468
  .get(ruby.to_symbol("wait"))
469
469
  .and_then(|v| BrowserWait::try_convert(v).ok())
470
- .unwrap_or_default(),
470
+ .unwrap_or(BrowserWait::NetworkIdle),
471
471
  wait_selector: kwargs
472
472
  .get(ruby.to_symbol("wait_selector"))
473
473
  .and_then(|v| String::try_convert(v).ok()),
@@ -557,7 +557,12 @@ impl BrowserConfig {
557
557
  pub struct CrawlConfig {
558
558
  max_depth: Option<usize>,
559
559
  max_pages: Option<usize>,
560
+ max_links_per_page: Option<usize>,
560
561
  max_concurrent: Option<usize>,
562
+ crawl_strategy: CrawlStrategyKind,
563
+ content_filter: Option<ContentFilterKind>,
564
+ bm25_query: Option<String>,
565
+ bm25_threshold: Option<f64>,
561
566
  respect_robots_txt: bool,
562
567
  soft_http_errors: bool,
563
568
  user_agent: Option<String>,
@@ -591,10 +596,13 @@ pub struct CrawlConfig {
591
596
  download_documents: bool,
592
597
  document_max_size: Option<usize>,
593
598
  document_mime_types: Vec<String>,
599
+ document_output_dir: Option<String>,
600
+ document_content_encoding: Option<DocumentContentEncoding>,
594
601
  warc_output: Option<String>,
595
602
  browser_profile: Option<String>,
596
603
  save_browser_profile: bool,
597
604
  ssrf: SsrfPolicy,
605
+ ssrf_deny_private_explicit: Option<bool>,
598
606
  }
599
607
 
600
608
  unsafe impl IntoValueFromNative for CrawlConfig {}
@@ -644,9 +652,25 @@ impl CrawlConfig {
644
652
  max_pages: kwargs
645
653
  .get(ruby.to_symbol("max_pages"))
646
654
  .and_then(|v| usize::try_convert(v).ok()),
655
+ max_links_per_page: kwargs
656
+ .get(ruby.to_symbol("max_links_per_page"))
657
+ .and_then(|v| usize::try_convert(v).ok()),
647
658
  max_concurrent: kwargs
648
659
  .get(ruby.to_symbol("max_concurrent"))
649
660
  .and_then(|v| usize::try_convert(v).ok()),
661
+ crawl_strategy: kwargs
662
+ .get(ruby.to_symbol("crawl_strategy"))
663
+ .and_then(|v| CrawlStrategyKind::try_convert(v).ok())
664
+ .unwrap_or(CrawlStrategyKind::Bfs),
665
+ content_filter: kwargs
666
+ .get(ruby.to_symbol("content_filter"))
667
+ .and_then(|v| ContentFilterKind::try_convert(v).ok()),
668
+ bm25_query: kwargs
669
+ .get(ruby.to_symbol("bm25_query"))
670
+ .and_then(|v| String::try_convert(v).ok()),
671
+ bm25_threshold: kwargs
672
+ .get(ruby.to_symbol("bm25_threshold"))
673
+ .and_then(|v| f64::try_convert(v).ok()),
650
674
  respect_robots_txt: kwargs
651
675
  .get(ruby.to_symbol("respect_robots_txt"))
652
676
  .and_then(|v| bool::try_convert(v).ok())
@@ -765,6 +789,12 @@ impl CrawlConfig {
765
789
  .get(ruby.to_symbol("document_mime_types"))
766
790
  .and_then(|v| <Vec<String>>::try_convert(v).ok())
767
791
  .unwrap_or_default(),
792
+ document_output_dir: kwargs
793
+ .get(ruby.to_symbol("document_output_dir"))
794
+ .and_then(|v| String::try_convert(v).ok()),
795
+ document_content_encoding: kwargs
796
+ .get(ruby.to_symbol("document_content_encoding"))
797
+ .and_then(|v| DocumentContentEncoding::try_convert(v).ok()),
768
798
  warc_output: kwargs
769
799
  .get(ruby.to_symbol("warc_output"))
770
800
  .and_then(|v| String::try_convert(v).ok()),
@@ -778,7 +808,10 @@ impl CrawlConfig {
778
808
  ssrf: kwargs
779
809
  .get(ruby.to_symbol("ssrf"))
780
810
  .and_then(|v| SsrfPolicy::try_convert(v).ok())
781
- .unwrap_or_default(),
811
+ .unwrap_or(crawlberg::SsrfPolicy::from_env().into()),
812
+ ssrf_deny_private_explicit: kwargs
813
+ .get(ruby.to_symbol("ssrf_deny_private_explicit"))
814
+ .and_then(|v| bool::try_convert(v).ok()),
782
815
  })
783
816
  }
784
817
 
@@ -790,10 +823,30 @@ impl CrawlConfig {
790
823
  self.max_pages
791
824
  }
792
825
 
826
+ fn max_links_per_page(&self) -> Option<usize> {
827
+ self.max_links_per_page
828
+ }
829
+
793
830
  fn max_concurrent(&self) -> Option<usize> {
794
831
  self.max_concurrent
795
832
  }
796
833
 
834
+ fn crawl_strategy(&self) -> CrawlStrategyKind {
835
+ self.crawl_strategy.clone()
836
+ }
837
+
838
+ fn content_filter(&self) -> Option<ContentFilterKind> {
839
+ self.content_filter.clone()
840
+ }
841
+
842
+ fn bm25_query(&self) -> Option<String> {
843
+ self.bm25_query.clone()
844
+ }
845
+
846
+ fn bm25_threshold(&self) -> Option<f64> {
847
+ self.bm25_threshold
848
+ }
849
+
797
850
  fn respect_robots_txt(&self) -> bool {
798
851
  self.respect_robots_txt
799
852
  }
@@ -922,6 +975,14 @@ impl CrawlConfig {
922
975
  self.document_mime_types.clone()
923
976
  }
924
977
 
978
+ fn document_output_dir(&self) -> Option<String> {
979
+ self.document_output_dir.clone()
980
+ }
981
+
982
+ fn document_content_encoding(&self) -> Option<DocumentContentEncoding> {
983
+ self.document_content_encoding.clone()
984
+ }
985
+
925
986
  fn warc_output(&self) -> Option<String> {
926
987
  self.warc_output.clone()
927
988
  }
@@ -938,6 +999,10 @@ impl CrawlConfig {
938
999
  self.ssrf.clone()
939
1000
  }
940
1001
 
1002
+ fn ssrf_deny_private_explicit(&self) -> Option<bool> {
1003
+ self.ssrf_deny_private_explicit
1004
+ }
1005
+
941
1006
  fn validate(&self) -> Result<(), Error> {
942
1007
  #[allow(clippy::needless_update)]
943
1008
  let core_self = crawlberg::CrawlConfig {
@@ -945,8 +1010,18 @@ impl CrawlConfig {
945
1010
 
946
1011
  max_pages: self.max_pages,
947
1012
 
1013
+ max_links_per_page: self.max_links_per_page,
1014
+
948
1015
  max_concurrent: self.max_concurrent,
949
1016
 
1017
+ crawl_strategy: self.crawl_strategy.clone().into(),
1018
+
1019
+ content_filter: self.content_filter.clone().map(Into::into),
1020
+
1021
+ bm25_query: self.bm25_query.clone(),
1022
+
1023
+ bm25_threshold: self.bm25_threshold,
1024
+
950
1025
  respect_robots_txt: self.respect_robots_txt,
951
1026
 
952
1027
  soft_http_errors: self.soft_http_errors,
@@ -1016,6 +1091,10 @@ impl CrawlConfig {
1016
1091
 
1017
1092
  document_mime_types: self.document_mime_types.clone(),
1018
1093
 
1094
+ document_output_dir: self.document_output_dir.clone().map(Into::into),
1095
+
1096
+ document_content_encoding: self.document_content_encoding.clone().map(Into::into),
1097
+
1019
1098
  warc_output: self.warc_output.clone().map(Into::into),
1020
1099
 
1021
1100
  browser_profile: self.browser_profile.clone(),
@@ -1024,6 +1103,8 @@ impl CrawlConfig {
1024
1103
 
1025
1104
  ssrf: self.ssrf.clone().into(),
1026
1105
 
1106
+ ssrf_deny_private_explicit: self.ssrf_deny_private_explicit,
1107
+
1027
1108
  ..Default::default()
1028
1109
  };
1029
1110
  let result = core_self.validate().map_err(|e| {
@@ -1123,6 +1204,9 @@ pub struct DownloadedDocument {
1123
1204
  filename: Option<String>,
1124
1205
  content_hash: String,
1125
1206
  headers: HashMap<String, String>,
1207
+ truncated: bool,
1208
+ content_path: Option<String>,
1209
+ content_base64: Option<String>,
1126
1210
  }
1127
1211
 
1128
1212
  unsafe impl IntoValueFromNative for DownloadedDocument {}
@@ -1189,6 +1273,16 @@ impl DownloadedDocument {
1189
1273
  .get(ruby.to_symbol("headers"))
1190
1274
  .and_then(|v| <HashMap<String, String>>::try_convert(v).ok())
1191
1275
  .unwrap_or_default(),
1276
+ truncated: kwargs
1277
+ .get(ruby.to_symbol("truncated"))
1278
+ .and_then(|v| bool::try_convert(v).ok())
1279
+ .unwrap_or_default(),
1280
+ content_path: kwargs
1281
+ .get(ruby.to_symbol("content_path"))
1282
+ .and_then(|v| String::try_convert(v).ok()),
1283
+ content_base64: kwargs
1284
+ .get(ruby.to_symbol("content_base64"))
1285
+ .and_then(|v| String::try_convert(v).ok()),
1192
1286
  })
1193
1287
  }
1194
1288
 
@@ -1215,6 +1309,18 @@ impl DownloadedDocument {
1215
1309
  fn headers(&self) -> HashMap<String, String> {
1216
1310
  self.headers.clone()
1217
1311
  }
1312
+
1313
+ fn truncated(&self) -> bool {
1314
+ self.truncated
1315
+ }
1316
+
1317
+ fn content_path(&self) -> Option<String> {
1318
+ self.content_path.clone()
1319
+ }
1320
+
1321
+ fn content_base64(&self) -> Option<String> {
1322
+ self.content_base64.clone()
1323
+ }
1218
1324
  }
1219
1325
 
1220
1326
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -1224,6 +1330,7 @@ pub struct InteractionResult {
1224
1330
  action_results: Vec<ActionResult>,
1225
1331
  final_html: String,
1226
1332
  final_url: String,
1333
+ screenshot_base64: Option<String>,
1227
1334
  }
1228
1335
 
1229
1336
  unsafe impl IntoValueFromNative for InteractionResult {}
@@ -1279,6 +1386,9 @@ impl InteractionResult {
1279
1386
  .get(ruby.to_symbol("final_url"))
1280
1387
  .and_then(|v| String::try_convert(v).ok())
1281
1388
  .unwrap_or_default(),
1389
+ screenshot_base64: kwargs
1390
+ .get(ruby.to_symbol("screenshot_base64"))
1391
+ .and_then(|v| String::try_convert(v).ok()),
1282
1392
  })
1283
1393
  }
1284
1394
 
@@ -1293,6 +1403,10 @@ impl InteractionResult {
1293
1403
  fn final_url(&self) -> String {
1294
1404
  self.final_url.clone()
1295
1405
  }
1406
+
1407
+ fn screenshot_base64(&self) -> Option<String> {
1408
+ self.screenshot_base64.clone()
1409
+ }
1296
1410
  }
1297
1411
 
1298
1412
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -1419,6 +1533,7 @@ pub struct ScrapeResult {
1419
1533
  markdown: Option<MarkdownResult>,
1420
1534
  extracted_data: Option<String>,
1421
1535
  extraction_meta: Option<ExtractionMeta>,
1536
+ screenshot_base64: Option<String>,
1422
1537
  downloaded_document: Option<DownloadedDocument>,
1423
1538
  browser: Option<BrowserExtras>,
1424
1539
  }
@@ -1561,6 +1676,9 @@ impl ScrapeResult {
1561
1676
  extraction_meta: kwargs
1562
1677
  .get(ruby.to_symbol("extraction_meta"))
1563
1678
  .and_then(|v| ExtractionMeta::try_convert(v).ok()),
1679
+ screenshot_base64: kwargs
1680
+ .get(ruby.to_symbol("screenshot_base64"))
1681
+ .and_then(|v| String::try_convert(v).ok()),
1564
1682
  downloaded_document: kwargs
1565
1683
  .get(ruby.to_symbol("downloaded_document"))
1566
1684
  .and_then(|v| DownloadedDocument::try_convert(v).ok()),
@@ -1674,6 +1792,10 @@ impl ScrapeResult {
1674
1792
  self.extraction_meta.clone()
1675
1793
  }
1676
1794
 
1795
+ fn screenshot_base64(&self) -> Option<String> {
1796
+ self.screenshot_base64.clone()
1797
+ }
1798
+
1677
1799
  fn downloaded_document(&self) -> Option<DownloadedDocument> {
1678
1800
  self.downloaded_document.clone()
1679
1801
  }
@@ -2379,7 +2501,7 @@ impl LinkInfo {
2379
2501
  link_type: kwargs
2380
2502
  .get(ruby.to_symbol("link_type"))
2381
2503
  .and_then(|v| LinkType::try_convert(v).ok())
2382
- .unwrap_or_default(),
2504
+ .unwrap_or(LinkType::Internal),
2383
2505
  rel: kwargs
2384
2506
  .get(ruby.to_symbol("rel"))
2385
2507
  .and_then(|v| String::try_convert(v).ok()),
@@ -2479,7 +2601,7 @@ impl ImageInfo {
2479
2601
  source: kwargs
2480
2602
  .get(ruby.to_symbol("source"))
2481
2603
  .and_then(|v| ImageSource::try_convert(v).ok())
2482
- .unwrap_or_default(),
2604
+ .unwrap_or(ImageSource::Img),
2483
2605
  })
2484
2606
  }
2485
2607
 
@@ -2564,7 +2686,7 @@ impl FeedInfo {
2564
2686
  feed_type: kwargs
2565
2687
  .get(ruby.to_symbol("feed_type"))
2566
2688
  .and_then(|v| FeedType::try_convert(v).ok())
2567
- .unwrap_or_default(),
2689
+ .unwrap_or(FeedType::Rss),
2568
2690
  })
2569
2691
  }
2570
2692
 
@@ -2814,7 +2936,7 @@ impl DownloadedAsset {
2814
2936
  asset_category: kwargs
2815
2937
  .get(ruby.to_symbol("asset_category"))
2816
2938
  .and_then(|v| AssetCategory::try_convert(v).ok())
2817
- .unwrap_or_default(),
2939
+ .unwrap_or(AssetCategory::Image),
2818
2940
  html_tag: kwargs
2819
2941
  .get(ruby.to_symbol("html_tag"))
2820
2942
  .and_then(|v| String::try_convert(v).ok()),
@@ -4358,6 +4480,7 @@ impl BatchCrawlResults {
4358
4480
  #[magnus::wrap(class = "Crawlberg::SsrfPolicy")]
4359
4481
  pub struct SsrfPolicy {
4360
4482
  deny_private: bool,
4483
+ allowlist: Vec<HostMatcher>,
4361
4484
  max_redirects: u8,
4362
4485
  }
4363
4486
 
@@ -4406,6 +4529,10 @@ impl SsrfPolicy {
4406
4529
  .get(ruby.to_symbol("deny_private"))
4407
4530
  .and_then(|v| bool::try_convert(v).ok())
4408
4531
  .unwrap_or(true),
4532
+ allowlist: kwargs
4533
+ .get(ruby.to_symbol("allowlist"))
4534
+ .and_then(|v| <Vec<HostMatcher>>::try_convert(v).ok())
4535
+ .unwrap_or_default(),
4409
4536
  max_redirects: kwargs
4410
4537
  .get(ruby.to_symbol("max_redirects"))
4411
4538
  .and_then(|v| u8::try_convert(v).ok())
@@ -4417,6 +4544,10 @@ impl SsrfPolicy {
4417
4544
  self.deny_private
4418
4545
  }
4419
4546
 
4547
+ fn allowlist(&self) -> Vec<HostMatcher> {
4548
+ self.allowlist.clone()
4549
+ }
4550
+
4420
4551
  fn max_redirects(&self) -> u8 {
4421
4552
  self.max_redirects
4422
4553
  }
@@ -4452,8 +4583,9 @@ impl magnus::IntoValue for BrowserMode {
4452
4583
  impl magnus::TryConvert for BrowserMode {
4453
4584
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4454
4585
  let s: String = magnus::TryConvert::try_convert(val)?;
4455
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4456
- // and a lowercase fallback so fixtures written in any of those styles work.
4586
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4587
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4588
+ // existing consumer code written in any of those styles keep working.
4457
4589
  match s.as_str() {
4458
4590
  "auto" | "Auto" => Ok(BrowserMode::Auto),
4459
4591
  "always" | "Always" => Ok(BrowserMode::Always),
@@ -4498,8 +4630,9 @@ impl magnus::IntoValue for BrowserWait {
4498
4630
  impl magnus::TryConvert for BrowserWait {
4499
4631
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4500
4632
  let s: String = magnus::TryConvert::try_convert(val)?;
4501
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4502
- // and a lowercase fallback so fixtures written in any of those styles work.
4633
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4634
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4635
+ // existing consumer code written in any of those styles keep working.
4503
4636
  match s.as_str() {
4504
4637
  "network_idle" | "NetworkIdle" => Ok(BrowserWait::NetworkIdle),
4505
4638
  "selector" | "Selector" => Ok(BrowserWait::Selector),
@@ -4541,8 +4674,9 @@ impl magnus::IntoValue for BrowserBackend {
4541
4674
  impl magnus::TryConvert for BrowserBackend {
4542
4675
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4543
4676
  let s: String = magnus::TryConvert::try_convert(val)?;
4544
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4545
- // and a lowercase fallback so fixtures written in any of those styles work.
4677
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4678
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4679
+ // existing consumer code written in any of those styles keep working.
4546
4680
  match s.as_str() {
4547
4681
  "chromiumoxide" | "Chromiumoxide" => Ok(BrowserBackend::Chromiumoxide),
4548
4682
  "native" | "Native" => Ok(BrowserBackend::Native),
@@ -4557,6 +4691,135 @@ impl magnus::TryConvert for BrowserBackend {
4557
4691
  unsafe impl IntoValueFromNative for BrowserBackend {}
4558
4692
  unsafe impl TryConvertOwned for BrowserBackend {}
4559
4693
 
4694
+ #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
4695
+ #[serde(rename_all = "snake_case")]
4696
+ pub enum DocumentContentEncoding {
4697
+ Base64,
4698
+ }
4699
+
4700
+ impl Default for DocumentContentEncoding {
4701
+ fn default() -> Self {
4702
+ Self::Base64
4703
+ }
4704
+ }
4705
+
4706
+ impl magnus::IntoValue for DocumentContentEncoding {
4707
+ fn into_value_with(self, handle: &Ruby) -> magnus::Value {
4708
+ let sym = match self {
4709
+ DocumentContentEncoding::Base64 => "base64",
4710
+ };
4711
+ handle.to_symbol(sym).into_value_with(handle)
4712
+ }
4713
+ }
4714
+
4715
+ impl magnus::TryConvert for DocumentContentEncoding {
4716
+ fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4717
+ let s: String = magnus::TryConvert::try_convert(val)?;
4718
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4719
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4720
+ // existing consumer code written in any of those styles keep working.
4721
+ match s.as_str() {
4722
+ "base64" | "Base64" => Ok(DocumentContentEncoding::Base64),
4723
+ other => Err(magnus::Error::new(
4724
+ unsafe { Ruby::get_unchecked() }.exception_arg_error(),
4725
+ format!("invalid DocumentContentEncoding value: {other}"),
4726
+ )),
4727
+ }
4728
+ }
4729
+ }
4730
+
4731
+ unsafe impl IntoValueFromNative for DocumentContentEncoding {}
4732
+ unsafe impl TryConvertOwned for DocumentContentEncoding {}
4733
+
4734
+ #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
4735
+ #[serde(rename_all = "snake_case")]
4736
+ pub enum CrawlStrategyKind {
4737
+ Bfs,
4738
+ Dfs,
4739
+ BestFirst,
4740
+ Adaptive,
4741
+ }
4742
+
4743
+ impl Default for CrawlStrategyKind {
4744
+ fn default() -> Self {
4745
+ Self::Bfs
4746
+ }
4747
+ }
4748
+
4749
+ impl magnus::IntoValue for CrawlStrategyKind {
4750
+ fn into_value_with(self, handle: &Ruby) -> magnus::Value {
4751
+ let sym = match self {
4752
+ CrawlStrategyKind::Bfs => "bfs",
4753
+ CrawlStrategyKind::Dfs => "dfs",
4754
+ CrawlStrategyKind::BestFirst => "best_first",
4755
+ CrawlStrategyKind::Adaptive => "adaptive",
4756
+ };
4757
+ handle.to_symbol(sym).into_value_with(handle)
4758
+ }
4759
+ }
4760
+
4761
+ impl magnus::TryConvert for CrawlStrategyKind {
4762
+ fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4763
+ let s: String = magnus::TryConvert::try_convert(val)?;
4764
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4765
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4766
+ // existing consumer code written in any of those styles keep working.
4767
+ match s.as_str() {
4768
+ "bfs" | "Bfs" => Ok(CrawlStrategyKind::Bfs),
4769
+ "dfs" | "Dfs" => Ok(CrawlStrategyKind::Dfs),
4770
+ "best_first" | "BestFirst" => Ok(CrawlStrategyKind::BestFirst),
4771
+ "adaptive" | "Adaptive" => Ok(CrawlStrategyKind::Adaptive),
4772
+ other => Err(magnus::Error::new(
4773
+ unsafe { Ruby::get_unchecked() }.exception_arg_error(),
4774
+ format!("invalid CrawlStrategyKind value: {other}"),
4775
+ )),
4776
+ }
4777
+ }
4778
+ }
4779
+
4780
+ unsafe impl IntoValueFromNative for CrawlStrategyKind {}
4781
+ unsafe impl TryConvertOwned for CrawlStrategyKind {}
4782
+
4783
+ #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
4784
+ #[serde(rename_all = "snake_case")]
4785
+ pub enum ContentFilterKind {
4786
+ Bm25,
4787
+ }
4788
+
4789
+ impl Default for ContentFilterKind {
4790
+ fn default() -> Self {
4791
+ Self::Bm25
4792
+ }
4793
+ }
4794
+
4795
+ impl magnus::IntoValue for ContentFilterKind {
4796
+ fn into_value_with(self, handle: &Ruby) -> magnus::Value {
4797
+ let sym = match self {
4798
+ ContentFilterKind::Bm25 => "bm25",
4799
+ };
4800
+ handle.to_symbol(sym).into_value_with(handle)
4801
+ }
4802
+ }
4803
+
4804
+ impl magnus::TryConvert for ContentFilterKind {
4805
+ fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4806
+ let s: String = magnus::TryConvert::try_convert(val)?;
4807
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4808
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4809
+ // existing consumer code written in any of those styles keep working.
4810
+ match s.as_str() {
4811
+ "bm25" | "Bm25" => Ok(ContentFilterKind::Bm25),
4812
+ other => Err(magnus::Error::new(
4813
+ unsafe { Ruby::get_unchecked() }.exception_arg_error(),
4814
+ format!("invalid ContentFilterKind value: {other}"),
4815
+ )),
4816
+ }
4817
+ }
4818
+ }
4819
+
4820
+ unsafe impl IntoValueFromNative for ContentFilterKind {}
4821
+ unsafe impl TryConvertOwned for ContentFilterKind {}
4822
+
4560
4823
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
4561
4824
  #[serde(tag = "type")]
4562
4825
  pub enum AuthConfig {
@@ -4652,8 +4915,9 @@ impl magnus::IntoValue for LinkType {
4652
4915
  impl magnus::TryConvert for LinkType {
4653
4916
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4654
4917
  let s: String = magnus::TryConvert::try_convert(val)?;
4655
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4656
- // and a lowercase fallback so fixtures written in any of those styles work.
4918
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4919
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4920
+ // existing consumer code written in any of those styles keep working.
4657
4921
  match s.as_str() {
4658
4922
  "internal" | "Internal" => Ok(LinkType::Internal),
4659
4923
  "external" | "External" => Ok(LinkType::External),
@@ -4692,8 +4956,8 @@ impl magnus::IntoValue for ImageSource {
4692
4956
  let sym = match self {
4693
4957
  ImageSource::Img => "img",
4694
4958
  ImageSource::PictureSource => "picture_source",
4695
- ImageSource::OgImage => "og_image",
4696
- ImageSource::TwitterImage => "twitter_image",
4959
+ ImageSource::OgImage => "og:image",
4960
+ ImageSource::TwitterImage => "twitter:image",
4697
4961
  };
4698
4962
  handle.to_symbol(sym).into_value_with(handle)
4699
4963
  }
@@ -4702,13 +4966,14 @@ impl magnus::IntoValue for ImageSource {
4702
4966
  impl magnus::TryConvert for ImageSource {
4703
4967
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4704
4968
  let s: String = magnus::TryConvert::try_convert(val)?;
4705
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4706
- // and a lowercase fallback so fixtures written in any of those styles work.
4969
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
4970
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
4971
+ // existing consumer code written in any of those styles keep working.
4707
4972
  match s.as_str() {
4708
4973
  "img" | "Img" => Ok(ImageSource::Img),
4709
4974
  "picture_source" | "PictureSource" => Ok(ImageSource::PictureSource),
4710
- "og_image" | "OgImage" => Ok(ImageSource::OgImage),
4711
- "twitter_image" | "TwitterImage" => Ok(ImageSource::TwitterImage),
4975
+ "og:image" | "og_image" | "OgImage" => Ok(ImageSource::OgImage),
4976
+ "twitter:image" | "twitter_image" | "TwitterImage" => Ok(ImageSource::TwitterImage),
4712
4977
  other => Err(magnus::Error::new(
4713
4978
  unsafe { Ruby::get_unchecked() }.exception_arg_error(),
4714
4979
  format!("invalid ImageSource value: {other}"),
@@ -4748,8 +5013,9 @@ impl magnus::IntoValue for FeedType {
4748
5013
  impl magnus::TryConvert for FeedType {
4749
5014
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4750
5015
  let s: String = magnus::TryConvert::try_convert(val)?;
4751
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4752
- // and a lowercase fallback so fixtures written in any of those styles work.
5016
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
5017
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
5018
+ // existing consumer code written in any of those styles keep working.
4753
5019
  match s.as_str() {
4754
5020
  "rss" | "Rss" => Ok(FeedType::Rss),
4755
5021
  "atom" | "Atom" => Ok(FeedType::Atom),
@@ -4807,8 +5073,9 @@ impl magnus::IntoValue for AssetCategory {
4807
5073
  impl magnus::TryConvert for AssetCategory {
4808
5074
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
4809
5075
  let s: String = magnus::TryConvert::try_convert(val)?;
4810
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
4811
- // and a lowercase fallback so fixtures written in any of those styles work.
5076
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
5077
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
5078
+ // existing consumer code written in any of those styles keep working.
4812
5079
  match s.as_str() {
4813
5080
  "document" | "Document" => Ok(AssetCategory::Document),
4814
5081
  "image" | "Image" => Ok(AssetCategory::Image),
@@ -5004,8 +5271,9 @@ impl magnus::IntoValue for ScrollDirection {
5004
5271
  impl magnus::TryConvert for ScrollDirection {
5005
5272
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
5006
5273
  let s: String = magnus::TryConvert::try_convert(val)?;
5007
- // Accept the serde wire name (snake_case), the PascalCase Rust variant name,
5008
- // and a lowercase fallback so fixtures written in any of those styles work.
5274
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
5275
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
5276
+ // existing consumer code written in any of those styles keep working.
5009
5277
  match s.as_str() {
5010
5278
  "up" | "Up" => Ok(ScrollDirection::Up),
5011
5279
  "down" | "Down" => Ok(ScrollDirection::Down),
@@ -5020,6 +5288,68 @@ impl magnus::TryConvert for ScrollDirection {
5020
5288
  unsafe impl IntoValueFromNative for ScrollDirection {}
5021
5289
  unsafe impl TryConvertOwned for ScrollDirection {}
5022
5290
 
5291
+ #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
5292
+ #[serde(tag = "type")]
5293
+ #[serde(rename_all = "snake_case")]
5294
+ pub enum HostMatcher {
5295
+ Exact { value: String },
5296
+ Suffix { value: String },
5297
+ Cidr { value: String },
5298
+ }
5299
+
5300
+ impl Default for HostMatcher {
5301
+ fn default() -> Self {
5302
+ Self::Exact {
5303
+ value: Default::default(),
5304
+ }
5305
+ }
5306
+ }
5307
+
5308
+ impl magnus::IntoValue for HostMatcher {
5309
+ fn into_value_with(self, handle: &Ruby) -> magnus::Value {
5310
+ match serde_json::to_value(&self) {
5311
+ Ok(v) => json_to_ruby(handle, v),
5312
+ Err(_) => handle.qnil().into_value_with(handle),
5313
+ }
5314
+ }
5315
+ }
5316
+
5317
+ impl magnus::TryConvert for HostMatcher {
5318
+ fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
5319
+ // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
5320
+ // For unit enums or when passed as a string, fall back to string-based conversion.
5321
+ let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
5322
+ s
5323
+ } else {
5324
+ val.funcall::<_, _, String>("to_json", ()).map_err(|e| {
5325
+ magnus::Error::new(
5326
+ unsafe { Ruby::get_unchecked() }.exception_type_error(),
5327
+ format!("no implicit conversion into HostMatcher: {}", e),
5328
+ )
5329
+ })?
5330
+ };
5331
+ // Try deserializing as JSON first (handles JSON strings like "\"markdown\"" or "{\"click\":{\"selector\":\"...\"}}\"")
5332
+ // For internally-tagged enums, a bare variant string is wrapped as {"<tag>": value}.
5333
+ // If that fails, try treating it as a plain string value and wrap in quotes
5334
+ // If both fail, try as Custom variant (for untagged enum support)
5335
+ serde_json::from_str(&json_str)
5336
+ .or_else(|_| serde_json::from_value(serde_json::json!({ "type": json_str })))
5337
+ .or_else(|_| serde_json::from_str(&format!("\"{json_str}\"")))
5338
+ .or_else(|_| {
5339
+ // Try as a JSON string for Custom variant (untagged enums accept any remaining value)
5340
+ match serde_json::to_value(&json_str) {
5341
+ Ok(val) => serde_json::from_value(val),
5342
+ Err(e) => Err(e),
5343
+ }
5344
+ })
5345
+ .map_err(|e| magnus::Error::new(unsafe { Ruby::get_unchecked() }.exception_type_error(), e.to_string()))
5346
+ }
5347
+ }
5348
+
5349
+ unsafe impl IntoValueFromNative for HostMatcher {}
5350
+ unsafe impl TryConvertOwned for HostMatcher {}
5351
+ impl HostMatcher {}
5352
+
5023
5353
  fn generate_citations(markdown: String) -> CitationResult {
5024
5354
  crawlberg::generate_citations(&markdown).into()
5025
5355
  }
@@ -5279,7 +5609,6 @@ fn batch_crawl_stream(engine: CrawlEngineHandle, req: BatchCrawlStreamRequest) -
5279
5609
  }
5280
5610
 
5281
5611
  #[allow(clippy::needless_update)]
5282
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5283
5612
  impl From<ExtractionMeta> for crawlberg::ExtractionMeta {
5284
5613
  fn from(val: ExtractionMeta) -> Self {
5285
5614
  Self {
@@ -5293,7 +5622,7 @@ impl From<ExtractionMeta> for crawlberg::ExtractionMeta {
5293
5622
  }
5294
5623
  }
5295
5624
 
5296
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5625
+ #[allow(clippy::redundant_closure)]
5297
5626
  impl From<crawlberg::ExtractionMeta> for ExtractionMeta {
5298
5627
  fn from(val: crawlberg::ExtractionMeta) -> Self {
5299
5628
  Self {
@@ -5307,7 +5636,6 @@ impl From<crawlberg::ExtractionMeta> for ExtractionMeta {
5307
5636
  }
5308
5637
 
5309
5638
  #[allow(clippy::needless_update)]
5310
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5311
5639
  impl From<ProxyConfig> for crawlberg::ProxyConfig {
5312
5640
  fn from(val: ProxyConfig) -> Self {
5313
5641
  Self {
@@ -5319,7 +5647,7 @@ impl From<ProxyConfig> for crawlberg::ProxyConfig {
5319
5647
  }
5320
5648
  }
5321
5649
 
5322
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5650
+ #[allow(clippy::redundant_closure)]
5323
5651
  impl From<crawlberg::ProxyConfig> for ProxyConfig {
5324
5652
  fn from(val: crawlberg::ProxyConfig) -> Self {
5325
5653
  Self {
@@ -5331,7 +5659,6 @@ impl From<crawlberg::ProxyConfig> for ProxyConfig {
5331
5659
  }
5332
5660
 
5333
5661
  #[allow(clippy::needless_update)]
5334
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5335
5662
  impl From<ContentConfig> for crawlberg::ContentConfig {
5336
5663
  fn from(val: ContentConfig) -> Self {
5337
5664
  Self {
@@ -5352,7 +5679,6 @@ impl From<ContentConfig> for crawlberg::ContentConfig {
5352
5679
  }
5353
5680
  }
5354
5681
 
5355
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5356
5682
  impl From<crawlberg::ContentConfig> for ContentConfig {
5357
5683
  fn from(val: crawlberg::ContentConfig) -> Self {
5358
5684
  Self {
@@ -5373,7 +5699,7 @@ impl From<crawlberg::ContentConfig> for ContentConfig {
5373
5699
  }
5374
5700
 
5375
5701
  #[allow(clippy::needless_update)]
5376
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5702
+ #[allow(clippy::useless_conversion)]
5377
5703
  impl From<BrowserConfig> for crawlberg::BrowserConfig {
5378
5704
  fn from(val: BrowserConfig) -> Self {
5379
5705
  Self {
@@ -5423,7 +5749,12 @@ impl From<CrawlConfig> for crawlberg::CrawlConfig {
5423
5749
  Self {
5424
5750
  max_depth: val.max_depth,
5425
5751
  max_pages: val.max_pages,
5752
+ max_links_per_page: val.max_links_per_page,
5426
5753
  max_concurrent: val.max_concurrent,
5754
+ crawl_strategy: val.crawl_strategy.into(),
5755
+ content_filter: val.content_filter.map(Into::into),
5756
+ bm25_query: val.bm25_query,
5757
+ bm25_threshold: val.bm25_threshold,
5427
5758
  respect_robots_txt: val.respect_robots_txt,
5428
5759
  soft_http_errors: val.soft_http_errors,
5429
5760
  user_agent: val.user_agent,
@@ -5460,10 +5791,13 @@ impl From<CrawlConfig> for crawlberg::CrawlConfig {
5460
5791
  download_documents: val.download_documents,
5461
5792
  document_max_size: val.document_max_size,
5462
5793
  document_mime_types: val.document_mime_types.into_iter().collect(),
5794
+ document_output_dir: val.document_output_dir.map(Into::into),
5795
+ document_content_encoding: val.document_content_encoding.map(Into::into),
5463
5796
  warc_output: val.warc_output.map(Into::into),
5464
5797
  browser_profile: val.browser_profile,
5465
5798
  save_browser_profile: val.save_browser_profile,
5466
5799
  ssrf: val.ssrf.into(),
5800
+ ssrf_deny_private_explicit: val.ssrf_deny_private_explicit,
5467
5801
  ..Default::default()
5468
5802
  }
5469
5803
  }
@@ -5475,7 +5809,12 @@ impl From<crawlberg::CrawlConfig> for CrawlConfig {
5475
5809
  Self {
5476
5810
  max_depth: val.max_depth,
5477
5811
  max_pages: val.max_pages,
5812
+ max_links_per_page: val.max_links_per_page,
5478
5813
  max_concurrent: val.max_concurrent,
5814
+ crawl_strategy: val.crawl_strategy.into(),
5815
+ content_filter: val.content_filter.map(Into::into),
5816
+ bm25_query: val.bm25_query.map(|v| v.to_string()),
5817
+ bm25_threshold: val.bm25_threshold,
5479
5818
  respect_robots_txt: val.respect_robots_txt,
5480
5819
  soft_http_errors: val.soft_http_errors,
5481
5820
  user_agent: val.user_agent.map(|v| v.to_string()),
@@ -5512,10 +5851,13 @@ impl From<crawlberg::CrawlConfig> for CrawlConfig {
5512
5851
  download_documents: val.download_documents,
5513
5852
  document_max_size: val.document_max_size,
5514
5853
  document_mime_types: val.document_mime_types.into_iter().collect(),
5854
+ document_output_dir: val.document_output_dir.map(|p| p.to_string_lossy().to_string()),
5855
+ document_content_encoding: val.document_content_encoding.map(Into::into),
5515
5856
  warc_output: val.warc_output.map(|p| p.to_string_lossy().to_string()),
5516
5857
  browser_profile: val.browser_profile.map(|v| v.to_string()),
5517
5858
  save_browser_profile: val.save_browser_profile,
5518
5859
  ssrf: val.ssrf.into(),
5860
+ ssrf_deny_private_explicit: val.ssrf_deny_private_explicit,
5519
5861
  }
5520
5862
  }
5521
5863
  }
@@ -5533,7 +5875,7 @@ impl From<BrowserExtras> for crawlberg::BrowserExtras {
5533
5875
  }
5534
5876
  }
5535
5877
 
5536
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5878
+ #[allow(clippy::useless_conversion)]
5537
5879
  impl From<crawlberg::BrowserExtras> for BrowserExtras {
5538
5880
  fn from(val: crawlberg::BrowserExtras) -> Self {
5539
5881
  Self {
@@ -5555,6 +5897,9 @@ impl From<DownloadedDocument> for crawlberg::DownloadedDocument {
5555
5897
  filename: val.filename.map(Into::into),
5556
5898
  content_hash: val.content_hash.into(),
5557
5899
  headers: val.headers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(),
5900
+ truncated: val.truncated,
5901
+ content_path: val.content_path,
5902
+ content_base64: val.content_base64,
5558
5903
  ..Default::default()
5559
5904
  }
5560
5905
  }
@@ -5570,18 +5915,22 @@ impl From<crawlberg::DownloadedDocument> for DownloadedDocument {
5570
5915
  filename: val.filename.as_ref().map(|v| v.to_string()),
5571
5916
  content_hash: val.content_hash.to_string(),
5572
5917
  headers: val.headers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(),
5918
+ truncated: val.truncated,
5919
+ content_path: val.content_path.map(|v| v.to_string()),
5920
+ content_base64: val.content_base64.map(|v| v.to_string()),
5573
5921
  }
5574
5922
  }
5575
5923
  }
5576
5924
 
5577
5925
  #[allow(clippy::needless_update)]
5578
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5926
+ #[allow(clippy::useless_conversion)]
5579
5927
  impl From<InteractionResult> for crawlberg::InteractionResult {
5580
5928
  fn from(val: InteractionResult) -> Self {
5581
5929
  Self {
5582
5930
  action_results: val.action_results.into_iter().map(Into::into).collect(),
5583
5931
  final_html: val.final_html,
5584
5932
  final_url: val.final_url,
5933
+ screenshot_base64: val.screenshot_base64,
5585
5934
  ..Default::default()
5586
5935
  }
5587
5936
  }
@@ -5594,6 +5943,7 @@ impl From<crawlberg::InteractionResult> for InteractionResult {
5594
5943
  action_results: val.action_results.into_iter().map(Into::into).collect(),
5595
5944
  final_html: val.final_html.to_string(),
5596
5945
  final_url: val.final_url.to_string(),
5946
+ screenshot_base64: val.screenshot_base64.map(|v| v.to_string()),
5597
5947
  }
5598
5948
  }
5599
5949
  }
@@ -5613,7 +5963,7 @@ impl From<ActionResult> for crawlberg::ActionResult {
5613
5963
  }
5614
5964
  }
5615
5965
 
5616
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5966
+ #[allow(clippy::redundant_closure)]
5617
5967
  impl From<crawlberg::ActionResult> for ActionResult {
5618
5968
  fn from(val: crawlberg::ActionResult) -> Self {
5619
5969
  Self {
@@ -5657,6 +6007,7 @@ impl From<ScrapeResult> for crawlberg::ScrapeResult {
5657
6007
  markdown: val.markdown.map(Into::into),
5658
6008
  extracted_data: val.extracted_data.as_ref().and_then(|s| serde_json::from_str(s).ok()),
5659
6009
  extraction_meta: val.extraction_meta.map(Into::into),
6010
+ screenshot_base64: val.screenshot_base64,
5660
6011
  downloaded_document: val.downloaded_document.map(Into::into),
5661
6012
  browser: val.browser.map(Into::into),
5662
6013
  ..Default::default()
@@ -5694,6 +6045,7 @@ impl From<crawlberg::ScrapeResult> for ScrapeResult {
5694
6045
  markdown: val.markdown.map(Into::into),
5695
6046
  extracted_data: val.extracted_data.as_ref().map(ToString::to_string),
5696
6047
  extraction_meta: val.extraction_meta.map(Into::into),
6048
+ screenshot_base64: val.screenshot_base64.map(|v| v.to_string()),
5697
6049
  downloaded_document: val.downloaded_document.map(Into::into),
5698
6050
  browser: val.browser.map(Into::into),
5699
6051
  }
@@ -5761,7 +6113,7 @@ impl From<crawlberg::CrawlPageResult> for CrawlPageResult {
5761
6113
  }
5762
6114
 
5763
6115
  #[allow(clippy::needless_update)]
5764
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6116
+ #[allow(clippy::useless_conversion)]
5765
6117
  impl From<CrawlResult> for crawlberg::CrawlResult {
5766
6118
  fn from(val: CrawlResult) -> Self {
5767
6119
  Self {
@@ -5795,7 +6147,6 @@ impl From<crawlberg::CrawlResult> for CrawlResult {
5795
6147
  }
5796
6148
 
5797
6149
  #[allow(clippy::needless_update)]
5798
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5799
6150
  impl From<SitemapUrl> for crawlberg::SitemapUrl {
5800
6151
  fn from(val: SitemapUrl) -> Self {
5801
6152
  Self {
@@ -5808,7 +6159,7 @@ impl From<SitemapUrl> for crawlberg::SitemapUrl {
5808
6159
  }
5809
6160
  }
5810
6161
 
5811
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6162
+ #[allow(clippy::redundant_closure)]
5812
6163
  impl From<crawlberg::SitemapUrl> for SitemapUrl {
5813
6164
  fn from(val: crawlberg::SitemapUrl) -> Self {
5814
6165
  Self {
@@ -5821,7 +6172,7 @@ impl From<crawlberg::SitemapUrl> for SitemapUrl {
5821
6172
  }
5822
6173
 
5823
6174
  #[allow(clippy::needless_update)]
5824
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6175
+ #[allow(clippy::useless_conversion)]
5825
6176
  impl From<MapResult> for crawlberg::MapResult {
5826
6177
  fn from(val: MapResult) -> Self {
5827
6178
  Self {
@@ -5831,7 +6182,7 @@ impl From<MapResult> for crawlberg::MapResult {
5831
6182
  }
5832
6183
  }
5833
6184
 
5834
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6185
+ #[allow(clippy::useless_conversion)]
5835
6186
  impl From<crawlberg::MapResult> for MapResult {
5836
6187
  fn from(val: crawlberg::MapResult) -> Self {
5837
6188
  Self {
@@ -5841,7 +6192,7 @@ impl From<crawlberg::MapResult> for MapResult {
5841
6192
  }
5842
6193
 
5843
6194
  #[allow(clippy::needless_update)]
5844
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6195
+ #[allow(clippy::redundant_closure)]
5845
6196
  impl From<MarkdownResult> for crawlberg::MarkdownResult {
5846
6197
  fn from(val: MarkdownResult) -> Self {
5847
6198
  Self {
@@ -5863,7 +6214,7 @@ impl From<MarkdownResult> for crawlberg::MarkdownResult {
5863
6214
  }
5864
6215
  }
5865
6216
 
5866
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6217
+ #[allow(clippy::redundant_closure)]
5867
6218
  impl From<crawlberg::MarkdownResult> for MarkdownResult {
5868
6219
  fn from(val: crawlberg::MarkdownResult) -> Self {
5869
6220
  Self {
@@ -5878,7 +6229,7 @@ impl From<crawlberg::MarkdownResult> for MarkdownResult {
5878
6229
  }
5879
6230
 
5880
6231
  #[allow(clippy::needless_update)]
5881
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6232
+ #[allow(clippy::useless_conversion)]
5882
6233
  impl From<LinkInfo> for crawlberg::LinkInfo {
5883
6234
  fn from(val: LinkInfo) -> Self {
5884
6235
  Self {
@@ -5906,7 +6257,7 @@ impl From<crawlberg::LinkInfo> for LinkInfo {
5906
6257
  }
5907
6258
 
5908
6259
  #[allow(clippy::needless_update)]
5909
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6260
+ #[allow(clippy::useless_conversion)]
5910
6261
  impl From<ImageInfo> for crawlberg::ImageInfo {
5911
6262
  fn from(val: ImageInfo) -> Self {
5912
6263
  Self {
@@ -5934,7 +6285,7 @@ impl From<crawlberg::ImageInfo> for ImageInfo {
5934
6285
  }
5935
6286
 
5936
6287
  #[allow(clippy::needless_update)]
5937
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6288
+ #[allow(clippy::useless_conversion)]
5938
6289
  impl From<FeedInfo> for crawlberg::FeedInfo {
5939
6290
  fn from(val: FeedInfo) -> Self {
5940
6291
  Self {
@@ -5958,7 +6309,6 @@ impl From<crawlberg::FeedInfo> for FeedInfo {
5958
6309
  }
5959
6310
 
5960
6311
  #[allow(clippy::needless_update)]
5961
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5962
6312
  impl From<JsonLdEntry> for crawlberg::JsonLdEntry {
5963
6313
  fn from(val: JsonLdEntry) -> Self {
5964
6314
  Self {
@@ -5970,7 +6320,7 @@ impl From<JsonLdEntry> for crawlberg::JsonLdEntry {
5970
6320
  }
5971
6321
  }
5972
6322
 
5973
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6323
+ #[allow(clippy::redundant_closure)]
5974
6324
  impl From<crawlberg::JsonLdEntry> for JsonLdEntry {
5975
6325
  fn from(val: crawlberg::JsonLdEntry) -> Self {
5976
6326
  Self {
@@ -5982,7 +6332,6 @@ impl From<crawlberg::JsonLdEntry> for JsonLdEntry {
5982
6332
  }
5983
6333
 
5984
6334
  #[allow(clippy::needless_update)]
5985
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
5986
6335
  impl From<CookieInfo> for crawlberg::CookieInfo {
5987
6336
  fn from(val: CookieInfo) -> Self {
5988
6337
  Self {
@@ -5995,7 +6344,7 @@ impl From<CookieInfo> for crawlberg::CookieInfo {
5995
6344
  }
5996
6345
  }
5997
6346
 
5998
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6347
+ #[allow(clippy::redundant_closure)]
5999
6348
  impl From<crawlberg::CookieInfo> for CookieInfo {
6000
6349
  fn from(val: crawlberg::CookieInfo) -> Self {
6001
6350
  Self {
@@ -6008,7 +6357,7 @@ impl From<crawlberg::CookieInfo> for CookieInfo {
6008
6357
  }
6009
6358
 
6010
6359
  #[allow(clippy::needless_update)]
6011
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6360
+ #[allow(clippy::useless_conversion)]
6012
6361
  impl From<DownloadedAsset> for crawlberg::DownloadedAsset {
6013
6362
  fn from(val: DownloadedAsset) -> Self {
6014
6363
  Self {
@@ -6038,7 +6387,6 @@ impl From<crawlberg::DownloadedAsset> for DownloadedAsset {
6038
6387
  }
6039
6388
 
6040
6389
  #[allow(clippy::needless_update)]
6041
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6042
6390
  impl From<ArticleMetadata> for crawlberg::ArticleMetadata {
6043
6391
  fn from(val: ArticleMetadata) -> Self {
6044
6392
  Self {
@@ -6052,7 +6400,7 @@ impl From<ArticleMetadata> for crawlberg::ArticleMetadata {
6052
6400
  }
6053
6401
  }
6054
6402
 
6055
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6403
+ #[allow(clippy::redundant_closure)]
6056
6404
  impl From<crawlberg::ArticleMetadata> for ArticleMetadata {
6057
6405
  fn from(val: crawlberg::ArticleMetadata) -> Self {
6058
6406
  Self {
@@ -6066,7 +6414,6 @@ impl From<crawlberg::ArticleMetadata> for ArticleMetadata {
6066
6414
  }
6067
6415
 
6068
6416
  #[allow(clippy::needless_update)]
6069
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6070
6417
  impl From<HreflangEntry> for crawlberg::HreflangEntry {
6071
6418
  fn from(val: HreflangEntry) -> Self {
6072
6419
  Self {
@@ -6077,7 +6424,6 @@ impl From<HreflangEntry> for crawlberg::HreflangEntry {
6077
6424
  }
6078
6425
  }
6079
6426
 
6080
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6081
6427
  impl From<crawlberg::HreflangEntry> for HreflangEntry {
6082
6428
  fn from(val: crawlberg::HreflangEntry) -> Self {
6083
6429
  Self {
@@ -6088,7 +6434,6 @@ impl From<crawlberg::HreflangEntry> for HreflangEntry {
6088
6434
  }
6089
6435
 
6090
6436
  #[allow(clippy::needless_update)]
6091
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6092
6437
  impl From<FaviconInfo> for crawlberg::FaviconInfo {
6093
6438
  fn from(val: FaviconInfo) -> Self {
6094
6439
  Self {
@@ -6101,7 +6446,7 @@ impl From<FaviconInfo> for crawlberg::FaviconInfo {
6101
6446
  }
6102
6447
  }
6103
6448
 
6104
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6449
+ #[allow(clippy::redundant_closure)]
6105
6450
  impl From<crawlberg::FaviconInfo> for FaviconInfo {
6106
6451
  fn from(val: crawlberg::FaviconInfo) -> Self {
6107
6452
  Self {
@@ -6114,7 +6459,6 @@ impl From<crawlberg::FaviconInfo> for FaviconInfo {
6114
6459
  }
6115
6460
 
6116
6461
  #[allow(clippy::needless_update)]
6117
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6118
6462
  impl From<HeadingInfo> for crawlberg::HeadingInfo {
6119
6463
  fn from(val: HeadingInfo) -> Self {
6120
6464
  Self {
@@ -6125,7 +6469,6 @@ impl From<HeadingInfo> for crawlberg::HeadingInfo {
6125
6469
  }
6126
6470
  }
6127
6471
 
6128
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6129
6472
  impl From<crawlberg::HeadingInfo> for HeadingInfo {
6130
6473
  fn from(val: crawlberg::HeadingInfo) -> Self {
6131
6474
  Self {
@@ -6136,7 +6479,6 @@ impl From<crawlberg::HeadingInfo> for HeadingInfo {
6136
6479
  }
6137
6480
 
6138
6481
  #[allow(clippy::needless_update)]
6139
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6140
6482
  impl From<ResponseMeta> for crawlberg::ResponseMeta {
6141
6483
  fn from(val: ResponseMeta) -> Self {
6142
6484
  Self {
@@ -6152,7 +6494,7 @@ impl From<ResponseMeta> for crawlberg::ResponseMeta {
6152
6494
  }
6153
6495
  }
6154
6496
 
6155
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6497
+ #[allow(clippy::redundant_closure)]
6156
6498
  impl From<crawlberg::ResponseMeta> for ResponseMeta {
6157
6499
  fn from(val: crawlberg::ResponseMeta) -> Self {
6158
6500
  Self {
@@ -6272,7 +6614,6 @@ impl From<crawlberg::PageMetadata> for PageMetadata {
6272
6614
  }
6273
6615
 
6274
6616
  #[allow(clippy::needless_update)]
6275
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6276
6617
  impl From<CrawlStreamRequest> for crawlberg::CrawlStreamRequest {
6277
6618
  fn from(val: CrawlStreamRequest) -> Self {
6278
6619
  Self {
@@ -6282,7 +6623,6 @@ impl From<CrawlStreamRequest> for crawlberg::CrawlStreamRequest {
6282
6623
  }
6283
6624
  }
6284
6625
 
6285
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6286
6626
  impl From<crawlberg::CrawlStreamRequest> for CrawlStreamRequest {
6287
6627
  fn from(val: crawlberg::CrawlStreamRequest) -> Self {
6288
6628
  Self {
@@ -6292,7 +6632,6 @@ impl From<crawlberg::CrawlStreamRequest> for CrawlStreamRequest {
6292
6632
  }
6293
6633
 
6294
6634
  #[allow(clippy::needless_update)]
6295
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6296
6635
  impl From<BatchCrawlStreamRequest> for crawlberg::BatchCrawlStreamRequest {
6297
6636
  fn from(val: BatchCrawlStreamRequest) -> Self {
6298
6637
  Self {
@@ -6302,7 +6641,6 @@ impl From<BatchCrawlStreamRequest> for crawlberg::BatchCrawlStreamRequest {
6302
6641
  }
6303
6642
  }
6304
6643
 
6305
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6306
6644
  impl From<crawlberg::BatchCrawlStreamRequest> for BatchCrawlStreamRequest {
6307
6645
  fn from(val: crawlberg::BatchCrawlStreamRequest) -> Self {
6308
6646
  Self {
@@ -6312,7 +6650,7 @@ impl From<crawlberg::BatchCrawlStreamRequest> for BatchCrawlStreamRequest {
6312
6650
  }
6313
6651
 
6314
6652
  #[allow(clippy::needless_update)]
6315
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6653
+ #[allow(clippy::useless_conversion)]
6316
6654
  impl From<CitationResult> for crawlberg::CitationResult {
6317
6655
  fn from(val: CitationResult) -> Self {
6318
6656
  Self {
@@ -6323,7 +6661,7 @@ impl From<CitationResult> for crawlberg::CitationResult {
6323
6661
  }
6324
6662
  }
6325
6663
 
6326
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6664
+ #[allow(clippy::useless_conversion)]
6327
6665
  impl From<crawlberg::CitationResult> for CitationResult {
6328
6666
  fn from(val: crawlberg::CitationResult) -> Self {
6329
6667
  Self {
@@ -6334,7 +6672,6 @@ impl From<crawlberg::CitationResult> for CitationResult {
6334
6672
  }
6335
6673
 
6336
6674
  #[allow(clippy::needless_update)]
6337
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6338
6675
  impl From<CitationReference> for crawlberg::CitationReference {
6339
6676
  fn from(val: CitationReference) -> Self {
6340
6677
  Self {
@@ -6346,7 +6683,6 @@ impl From<CitationReference> for crawlberg::CitationReference {
6346
6683
  }
6347
6684
  }
6348
6685
 
6349
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6350
6686
  impl From<crawlberg::CitationReference> for CitationReference {
6351
6687
  fn from(val: crawlberg::CitationReference) -> Self {
6352
6688
  Self {
@@ -6358,7 +6694,7 @@ impl From<crawlberg::CitationReference> for CitationReference {
6358
6694
  }
6359
6695
 
6360
6696
  #[allow(clippy::needless_update)]
6361
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6697
+ #[allow(clippy::useless_conversion)]
6362
6698
  impl From<BatchScrapeResult> for crawlberg::BatchScrapeResult {
6363
6699
  fn from(val: BatchScrapeResult) -> Self {
6364
6700
  Self {
@@ -6382,7 +6718,7 @@ impl From<crawlberg::BatchScrapeResult> for BatchScrapeResult {
6382
6718
  }
6383
6719
 
6384
6720
  #[allow(clippy::needless_update)]
6385
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6721
+ #[allow(clippy::useless_conversion)]
6386
6722
  impl From<BatchCrawlResult> for crawlberg::BatchCrawlResult {
6387
6723
  fn from(val: BatchCrawlResult) -> Self {
6388
6724
  Self {
@@ -6406,7 +6742,7 @@ impl From<crawlberg::BatchCrawlResult> for BatchCrawlResult {
6406
6742
  }
6407
6743
 
6408
6744
  #[allow(clippy::needless_update)]
6409
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6745
+ #[allow(clippy::useless_conversion)]
6410
6746
  impl From<BatchScrapeResults> for crawlberg::BatchScrapeResults {
6411
6747
  fn from(val: BatchScrapeResults) -> Self {
6412
6748
  Self {
@@ -6419,7 +6755,7 @@ impl From<BatchScrapeResults> for crawlberg::BatchScrapeResults {
6419
6755
  }
6420
6756
  }
6421
6757
 
6422
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6758
+ #[allow(clippy::useless_conversion)]
6423
6759
  impl From<crawlberg::BatchScrapeResults> for BatchScrapeResults {
6424
6760
  fn from(val: crawlberg::BatchScrapeResults) -> Self {
6425
6761
  Self {
@@ -6432,7 +6768,7 @@ impl From<crawlberg::BatchScrapeResults> for BatchScrapeResults {
6432
6768
  }
6433
6769
 
6434
6770
  #[allow(clippy::needless_update)]
6435
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6771
+ #[allow(clippy::useless_conversion)]
6436
6772
  impl From<BatchCrawlResults> for crawlberg::BatchCrawlResults {
6437
6773
  fn from(val: BatchCrawlResults) -> Self {
6438
6774
  Self {
@@ -6445,7 +6781,7 @@ impl From<BatchCrawlResults> for crawlberg::BatchCrawlResults {
6445
6781
  }
6446
6782
  }
6447
6783
 
6448
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6784
+ #[allow(clippy::useless_conversion)]
6449
6785
  impl From<crawlberg::BatchCrawlResults> for BatchCrawlResults {
6450
6786
  fn from(val: crawlberg::BatchCrawlResults) -> Self {
6451
6787
  Self {
@@ -6458,22 +6794,24 @@ impl From<crawlberg::BatchCrawlResults> for BatchCrawlResults {
6458
6794
  }
6459
6795
 
6460
6796
  #[allow(clippy::needless_update)]
6461
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6797
+ #[allow(clippy::useless_conversion)]
6462
6798
  impl From<SsrfPolicy> for crawlberg::SsrfPolicy {
6463
6799
  fn from(val: SsrfPolicy) -> Self {
6464
6800
  Self {
6465
6801
  deny_private: val.deny_private,
6802
+ allowlist: val.allowlist.into_iter().map(Into::into).collect(),
6466
6803
  max_redirects: val.max_redirects,
6467
6804
  ..Default::default()
6468
6805
  }
6469
6806
  }
6470
6807
  }
6471
6808
 
6472
- #[allow(clippy::redundant_closure, clippy::useless_conversion)]
6809
+ #[allow(clippy::useless_conversion)]
6473
6810
  impl From<crawlberg::SsrfPolicy> for SsrfPolicy {
6474
6811
  fn from(val: crawlberg::SsrfPolicy) -> Self {
6475
6812
  Self {
6476
6813
  deny_private: val.deny_private,
6814
+ allowlist: val.allowlist.into_iter().map(Into::into).collect(),
6477
6815
  max_redirects: val.max_redirects,
6478
6816
  }
6479
6817
  }
@@ -6539,6 +6877,60 @@ impl From<crawlberg::BrowserBackend> for BrowserBackend {
6539
6877
  }
6540
6878
  }
6541
6879
 
6880
+ impl From<DocumentContentEncoding> for crawlberg::DocumentContentEncoding {
6881
+ fn from(val: DocumentContentEncoding) -> Self {
6882
+ match val {
6883
+ DocumentContentEncoding::Base64 => Self::Base64,
6884
+ }
6885
+ }
6886
+ }
6887
+
6888
+ impl From<crawlberg::DocumentContentEncoding> for DocumentContentEncoding {
6889
+ fn from(val: crawlberg::DocumentContentEncoding) -> Self {
6890
+ match val {
6891
+ crawlberg::DocumentContentEncoding::Base64 => Self::Base64,
6892
+ }
6893
+ }
6894
+ }
6895
+
6896
+ impl From<CrawlStrategyKind> for crawlberg::CrawlStrategyKind {
6897
+ fn from(val: CrawlStrategyKind) -> Self {
6898
+ match val {
6899
+ CrawlStrategyKind::Bfs => Self::Bfs,
6900
+ CrawlStrategyKind::Dfs => Self::Dfs,
6901
+ CrawlStrategyKind::BestFirst => Self::BestFirst,
6902
+ CrawlStrategyKind::Adaptive => Self::Adaptive,
6903
+ }
6904
+ }
6905
+ }
6906
+
6907
+ impl From<crawlberg::CrawlStrategyKind> for CrawlStrategyKind {
6908
+ fn from(val: crawlberg::CrawlStrategyKind) -> Self {
6909
+ match val {
6910
+ crawlberg::CrawlStrategyKind::Bfs => Self::Bfs,
6911
+ crawlberg::CrawlStrategyKind::Dfs => Self::Dfs,
6912
+ crawlberg::CrawlStrategyKind::BestFirst => Self::BestFirst,
6913
+ crawlberg::CrawlStrategyKind::Adaptive => Self::Adaptive,
6914
+ }
6915
+ }
6916
+ }
6917
+
6918
+ impl From<ContentFilterKind> for crawlberg::ContentFilterKind {
6919
+ fn from(val: ContentFilterKind) -> Self {
6920
+ match val {
6921
+ ContentFilterKind::Bm25 => Self::Bm25,
6922
+ }
6923
+ }
6924
+ }
6925
+
6926
+ impl From<crawlberg::ContentFilterKind> for ContentFilterKind {
6927
+ fn from(val: crawlberg::ContentFilterKind) -> Self {
6928
+ match val {
6929
+ crawlberg::ContentFilterKind::Bm25 => Self::Bm25,
6930
+ }
6931
+ }
6932
+ }
6933
+
6542
6934
  impl From<AuthConfig> for crawlberg::AuthConfig {
6543
6935
  fn from(val: AuthConfig) -> Self {
6544
6936
  match val {
@@ -6768,6 +7160,32 @@ impl From<crawlberg::ScrollDirection> for ScrollDirection {
6768
7160
  }
6769
7161
  }
6770
7162
 
7163
+ impl From<HostMatcher> for crawlberg::HostMatcher {
7164
+ fn from(val: HostMatcher) -> Self {
7165
+ match val {
7166
+ HostMatcher::Exact { value } => Self::Exact { value: value },
7167
+ HostMatcher::Suffix { value } => Self::Suffix { value: value },
7168
+ HostMatcher::Cidr { value } => Self::Cidr { value: value },
7169
+ }
7170
+ }
7171
+ }
7172
+
7173
+ impl From<crawlberg::HostMatcher> for HostMatcher {
7174
+ fn from(val: crawlberg::HostMatcher) -> Self {
7175
+ match val {
7176
+ crawlberg::HostMatcher::Exact { value } => Self::Exact {
7177
+ value: value.to_string(),
7178
+ },
7179
+ crawlberg::HostMatcher::Suffix { value } => Self::Suffix {
7180
+ value: value.to_string(),
7181
+ },
7182
+ crawlberg::HostMatcher::Cidr { value } => Self::Cidr {
7183
+ value: value.to_string(),
7184
+ },
7185
+ }
7186
+ }
7187
+ }
7188
+
6771
7189
  /// Convert a `crawlberg::CrawlError` error to a Magnus runtime error.
6772
7190
  #[allow(dead_code)]
6773
7191
  fn crawl_error_to_magnus_err(e: crawlberg::CrawlError) -> magnus::Error {
@@ -7049,8 +7467,18 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7049
7467
 
7050
7468
  class.define_method("max_pages", method!(CrawlConfig::max_pages, 0))?;
7051
7469
 
7470
+ class.define_method("max_links_per_page", method!(CrawlConfig::max_links_per_page, 0))?;
7471
+
7052
7472
  class.define_method("max_concurrent", method!(CrawlConfig::max_concurrent, 0))?;
7053
7473
 
7474
+ class.define_method("crawl_strategy", method!(CrawlConfig::crawl_strategy, 0))?;
7475
+
7476
+ class.define_method("content_filter", method!(CrawlConfig::content_filter, 0))?;
7477
+
7478
+ class.define_method("bm25_query", method!(CrawlConfig::bm25_query, 0))?;
7479
+
7480
+ class.define_method("bm25_threshold", method!(CrawlConfig::bm25_threshold, 0))?;
7481
+
7054
7482
  class.define_method("respect_robots_txt", method!(CrawlConfig::respect_robots_txt, 0))?;
7055
7483
 
7056
7484
  class.define_method("soft_http_errors", method!(CrawlConfig::soft_http_errors, 0))?;
@@ -7115,6 +7543,13 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7115
7543
 
7116
7544
  class.define_method("document_mime_types", method!(CrawlConfig::document_mime_types, 0))?;
7117
7545
 
7546
+ class.define_method("document_output_dir", method!(CrawlConfig::document_output_dir, 0))?;
7547
+
7548
+ class.define_method(
7549
+ "document_content_encoding",
7550
+ method!(CrawlConfig::document_content_encoding, 0),
7551
+ )?;
7552
+
7118
7553
  class.define_method("warc_output", method!(CrawlConfig::warc_output, 0))?;
7119
7554
 
7120
7555
  class.define_method("browser_profile", method!(CrawlConfig::browser_profile, 0))?;
@@ -7123,6 +7558,11 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7123
7558
 
7124
7559
  class.define_method("ssrf", method!(CrawlConfig::ssrf, 0))?;
7125
7560
 
7561
+ class.define_method(
7562
+ "ssrf_deny_private_explicit",
7563
+ method!(CrawlConfig::ssrf_deny_private_explicit, 0),
7564
+ )?;
7565
+
7126
7566
  class.define_method("validate", method!(CrawlConfig::validate, 0))?;
7127
7567
 
7128
7568
  let class = module.define_class("BrowserExtras", ruby.class_object())?;
@@ -7151,6 +7591,12 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7151
7591
 
7152
7592
  class.define_method("headers", method!(DownloadedDocument::headers, 0))?;
7153
7593
 
7594
+ class.define_method("truncated", method!(DownloadedDocument::truncated, 0))?;
7595
+
7596
+ class.define_method("content_path", method!(DownloadedDocument::content_path, 0))?;
7597
+
7598
+ class.define_method("content_base64", method!(DownloadedDocument::content_base64, 0))?;
7599
+
7154
7600
  let class = module.define_class("InteractionResult", ruby.class_object())?;
7155
7601
 
7156
7602
  class.define_singleton_method("new", function!(InteractionResult::new, -1))?;
@@ -7161,6 +7607,8 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7161
7607
 
7162
7608
  class.define_method("final_url", method!(InteractionResult::final_url, 0))?;
7163
7609
 
7610
+ class.define_method("screenshot_base64", method!(InteractionResult::screenshot_base64, 0))?;
7611
+
7164
7612
  let class = module.define_class("ActionResult", ruby.class_object())?;
7165
7613
 
7166
7614
  class.define_singleton_method("new", function!(ActionResult::new, -1))?;
@@ -7231,6 +7679,8 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7231
7679
 
7232
7680
  class.define_method("extraction_meta", method!(ScrapeResult::extraction_meta, 0))?;
7233
7681
 
7682
+ class.define_method("screenshot_base64", method!(ScrapeResult::screenshot_base64, 0))?;
7683
+
7234
7684
  class.define_method("downloaded_document", method!(ScrapeResult::downloaded_document, 0))?;
7235
7685
 
7236
7686
  class.define_method("browser", method!(ScrapeResult::browser, 0))?;
@@ -7655,6 +8105,8 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
7655
8105
 
7656
8106
  class.define_method("deny_private", method!(SsrfPolicy::deny_private, 0))?;
7657
8107
 
8108
+ class.define_method("allowlist", method!(SsrfPolicy::allowlist, 0))?;
8109
+
7658
8110
  class.define_method("max_redirects", method!(SsrfPolicy::max_redirects, 0))?;
7659
8111
 
7660
8112
  let class = module.define_class("CrawlStreamIterator", ruby.class_object())?;