fetch_util 0.6.2 → 0.8.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +187 -0
  3. data/README.md +2 -1
  4. data/lib/fetch_util/assets/extract.js +1 -1
  5. data/lib/fetch_util/browser/navigation/navigator_patch.rb +2 -0
  6. data/lib/fetch_util/browser/navigation/shadow_root_patch.rb +85 -0
  7. data/lib/fetch_util/browser/navigation.rb +2 -0
  8. data/lib/fetch_util/browser/site_stabilization/communities/reddit.rb +71 -0
  9. data/lib/fetch_util/browser/site_stabilization/communities.rb +13 -0
  10. data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_pull_resource_modules.rb → forges/bitbucket/pulls/resource_modules.rb} +5 -5
  11. data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_pull_resources.rb → forges/bitbucket/pulls/resources.rb} +3 -34
  12. data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_threads.rb → forges/bitbucket/threads/index.rb} +3 -34
  13. data/lib/fetch_util/browser/site_stabilization/forges/gitea/stabilization.rb +31 -0
  14. data/lib/fetch_util/browser/site_stabilization/forges/observation_state.rb +59 -0
  15. data/lib/fetch_util/browser/site_stabilization/forges.rb +109 -0
  16. data/lib/fetch_util/browser/site_stabilization/marketplaces/ebay.rb +56 -0
  17. data/lib/fetch_util/browser/site_stabilization/marketplaces.rb +13 -0
  18. data/lib/fetch_util/browser/site_stabilization/{facebook_stabilization.rb → social/facebook.rb} +7 -0
  19. data/lib/fetch_util/browser/site_stabilization/{social_platforms.rb → social/instagram.rb} +6 -8
  20. data/lib/fetch_util/browser/site_stabilization/social.rb +15 -0
  21. data/lib/fetch_util/browser/site_stabilization/{travel_and_lodging.rb → travel/lodging.rb} +7 -9
  22. data/lib/fetch_util/browser/site_stabilization/travel.rb +13 -0
  23. data/lib/fetch_util/browser/site_stabilization.rb +10 -97
  24. data/lib/fetch_util/browser/stabilization/page_flow.rb +7 -5
  25. data/lib/fetch_util/browser.rb +6 -0
  26. data/lib/fetch_util/extractor.rb +115 -9
  27. data/lib/fetch_util/raw_docs_fallback.rb +5 -1
  28. data/lib/fetch_util/regulatory/cache_store.rb +5 -3
  29. data/lib/fetch_util/regulatory/cache_validation.rb +24 -0
  30. data/lib/fetch_util/regulatory/tdm_policy.rb +1 -1
  31. data/lib/fetch_util/regulatory.rb +1 -0
  32. data/lib/fetch_util/version.rb +1 -1
  33. metadata +57 -48
  34. data/lib/fetch_util/browser/site_stabilization/community_and_marketplace.rb +0 -117
  35. data/lib/fetch_util/browser/site_stabilization/gitea_family_stabilization.rb +0 -60
  36. /data/lib/fetch_util/browser/site_stabilization/{azure_devops_pr_commit_details_script.rb → forges/azure_devops/pull_requests/commit_details_script.rb} +0 -0
  37. /data/lib/fetch_util/browser/site_stabilization/{azure_devops_pr_product_state_script.rb → forges/azure_devops/pull_requests/product_state_script.rb} +0 -0
  38. /data/lib/fetch_util/browser/site_stabilization/{azure_devops_pr_request_helpers_script.rb → forges/azure_devops/pull_requests/request_helpers_script.rb} +0 -0
  39. /data/lib/fetch_util/browser/site_stabilization/{azure_devops_pr_request_state_script.rb → forges/azure_devops/pull_requests/request_state_script.rb} +0 -0
  40. /data/lib/fetch_util/browser/site_stabilization/{azure_devops_pr_stabilization.rb → forges/azure_devops/pull_requests/stabilization.rb} +0 -0
  41. /data/lib/fetch_util/browser/site_stabilization/{azure_devops_pr_state.rb → forges/azure_devops/pull_requests/state.rb} +0 -0
  42. /data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_pull_diff_product_state.rb → forges/bitbucket/pulls/diff/product_state.rb} +0 -0
  43. /data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_pull_diff_request_state.rb → forges/bitbucket/pulls/diff/request_state.rb} +0 -0
  44. /data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_pull_diff_state.rb → forges/bitbucket/pulls/diff/state.rb} +0 -0
  45. /data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_pull_resource_state.rb → forges/bitbucket/pulls/resource_state.rb} +0 -0
  46. /data/lib/fetch_util/browser/site_stabilization/{bitbucket_cloud_thread_state.rb → forges/bitbucket/threads/state.rb} +0 -0
  47. /data/lib/fetch_util/browser/site_stabilization/{gerrit_change_product_state_script.rb → forges/gerrit/changes/product_state_script.rb} +0 -0
  48. /data/lib/fetch_util/browser/site_stabilization/{gerrit_change_request_state_script.rb → forges/gerrit/changes/request_state_script.rb} +0 -0
  49. /data/lib/fetch_util/browser/site_stabilization/{gerrit_change_stabilization.rb → forges/gerrit/changes/stabilization.rb} +0 -0
  50. /data/lib/fetch_util/browser/site_stabilization/{gerrit_change_state.rb → forges/gerrit/changes/state.rb} +0 -0
  51. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_fetch_state_script.rb → forges/gerrit/resources/fetch_state_script.rb} +0 -0
  52. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_product_state_script.rb → forges/gerrit/resources/product_state_script.rb} +0 -0
  53. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_request_state_script.rb → forges/gerrit/resources/request_state_script.rb} +0 -0
  54. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_route_state_script.rb → forges/gerrit/resources/route_state_script.rb} +0 -0
  55. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_stabilization.rb → forges/gerrit/resources/stabilization.rb} +0 -0
  56. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_state.rb → forges/gerrit/resources/state.rb} +0 -0
  57. /data/lib/fetch_util/browser/site_stabilization/{gerrit_file_resource_validation_state_script.rb → forges/gerrit/resources/validation_state_script.rb} +0 -0
  58. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_pull_resource_product_state.rb → forges/gitea/pulls/product_state.rb} +0 -0
  59. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_pull_resources.rb → forges/gitea/pulls/resources.rb} +0 -0
  60. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_pull_resource_state.rb → forges/gitea/pulls/state.rb} +0 -0
  61. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_threads.rb → forges/gitea/threads/index.rb} +0 -0
  62. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_thread_product_state.rb → forges/gitea/threads/product_state.rb} +0 -0
  63. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_thread_timeline_state.rb → forges/gitea/threads/timeline_state.rb} +0 -0
  64. /data/lib/fetch_util/browser/site_stabilization/{gitea_family_visibility_script.rb → forges/gitea/visibility_script.rb} +0 -0
  65. /data/lib/fetch_util/browser/site_stabilization/{github_pull_resource_stabilization.rb → forges/github/pull_resource_stabilization.rb} +0 -0
  66. /data/lib/fetch_util/browser/site_stabilization/{github_pull_resources.rb → forges/github/pull_resources.rb} +0 -0
  67. /data/lib/fetch_util/browser/site_stabilization/{github_threads.rb → forges/github/threads.rb} +0 -0
  68. /data/lib/fetch_util/browser/site_stabilization/{gitlab_merge_request_resource_state.rb → forges/gitlab/merge_requests/resource_state.rb} +0 -0
  69. /data/lib/fetch_util/browser/site_stabilization/{gitlab_merge_request_resource_state_script.rb → forges/gitlab/merge_requests/resource_state_script.rb} +0 -0
  70. /data/lib/fetch_util/browser/site_stabilization/{gitlab_merge_request_resource_visibility_script.rb → forges/gitlab/merge_requests/resource_visibility_script.rb} +0 -0
  71. /data/lib/fetch_util/browser/site_stabilization/{gitlab_merge_request_resources.rb → forges/gitlab/merge_requests/resources.rb} +0 -0
  72. /data/lib/fetch_util/browser/site_stabilization/{gitlab_repo.rb → forges/gitlab/repository.rb} +0 -0
  73. /data/lib/fetch_util/browser/site_stabilization/{gitlab_stabilization.rb → forges/gitlab/stabilization.rb} +0 -0
  74. /data/lib/fetch_util/browser/site_stabilization/{gitlab_threads.rb → forges/gitlab/threads.rb} +0 -0
@@ -3,15 +3,13 @@
3
3
  module FetchUtil
4
4
  class Browser
5
5
  module SiteStabilization
6
- module TravelAndLodging
7
- TRAVEL_LODGING_STABILIZATION_PROFILES = {
8
- stabilize_lodging_detail: {
9
- host: %w[airbnb.com booking.com],
10
- path_query: ->(uri) { uri.path.match?(%r{/(?:rooms|hotel)/}) },
11
- strategy: :stabilize_lodging_detail,
12
- notes: "Wait for client-rendered lodging detail cues without bypassing access controls.",
13
- tests: "spec/fetch_util/browser_stabilization_spec.rb"
14
- }
6
+ module LodgingStabilization
7
+ LODGING_STABILIZATION_PROFILE = {
8
+ host: %w[airbnb.com booking.com],
9
+ path_query: ->(uri) { uri.path.match?(%r{/(?:rooms|hotel)/}) },
10
+ strategy: :stabilize_lodging_detail,
11
+ notes: "Wait for client-rendered lodging detail cues without bypassing access controls.",
12
+ tests: "spec/fetch_util/browser_stabilization_spec.rb"
15
13
  }.freeze
16
14
 
17
15
  private
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FetchUtil
4
+ class Browser
5
+ module SiteStabilization
6
+ autoload :LodgingStabilization, "fetch_util/browser/site_stabilization/travel/lodging"
7
+
8
+ module TravelStabilization
9
+ include LodgingStabilization
10
+ end
11
+ end
12
+ end
13
+ end
@@ -3,104 +3,17 @@
3
3
  module FetchUtil
4
4
  class Browser
5
5
  module SiteStabilization
6
- autoload :AzureDevopsPrProductStateScript,
7
- "fetch_util/browser/site_stabilization/azure_devops_pr_product_state_script"
8
- autoload :AzureDevopsPrCommitDetailsScript,
9
- "fetch_util/browser/site_stabilization/azure_devops_pr_commit_details_script"
10
- autoload :AzureDevopsPrRequestHelpersScript,
11
- "fetch_util/browser/site_stabilization/azure_devops_pr_request_helpers_script"
12
- autoload :AzureDevopsPrRequestStateScript,
13
- "fetch_util/browser/site_stabilization/azure_devops_pr_request_state_script"
14
- autoload :AzureDevopsPrState, "fetch_util/browser/site_stabilization/azure_devops_pr_state"
15
- autoload :AzureDevopsPrStabilization,
16
- "fetch_util/browser/site_stabilization/azure_devops_pr_stabilization"
17
- autoload :BitbucketCloudThreadState,
18
- "fetch_util/browser/site_stabilization/bitbucket_cloud_thread_state"
19
- autoload :BitbucketCloudThreads, "fetch_util/browser/site_stabilization/bitbucket_cloud_threads"
20
- autoload :BitbucketCloudPullResourceModules,
21
- "fetch_util/browser/site_stabilization/bitbucket_cloud_pull_resource_modules"
22
- autoload :CommunityAndMarketplace, "fetch_util/browser/site_stabilization/community_and_marketplace"
23
- autoload :FacebookStabilization, "fetch_util/browser/site_stabilization/facebook_stabilization"
24
- autoload :GerritChangeProductStateScript,
25
- "fetch_util/browser/site_stabilization/gerrit_change_product_state_script"
26
- autoload :GerritChangeRequestStateScript,
27
- "fetch_util/browser/site_stabilization/gerrit_change_request_state_script"
28
- autoload :GerritChangeState, "fetch_util/browser/site_stabilization/gerrit_change_state"
29
- autoload :GerritChangeStabilization,
30
- "fetch_util/browser/site_stabilization/gerrit_change_stabilization"
31
- autoload :GerritFileResourceProductStateScript,
32
- "fetch_util/browser/site_stabilization/gerrit_file_resource_product_state_script"
33
- autoload :GerritFileResourceRouteStateScript,
34
- "fetch_util/browser/site_stabilization/gerrit_file_resource_route_state_script"
35
- autoload :GerritFileResourceFetchStateScript,
36
- "fetch_util/browser/site_stabilization/gerrit_file_resource_fetch_state_script"
37
- autoload :GerritFileResourceValidationStateScript,
38
- "fetch_util/browser/site_stabilization/gerrit_file_resource_validation_state_script"
39
- autoload :GerritFileResourceRequestStateScript,
40
- "fetch_util/browser/site_stabilization/gerrit_file_resource_request_state_script"
41
- autoload :GerritFileResourceState,
42
- "fetch_util/browser/site_stabilization/gerrit_file_resource_state"
43
- autoload :GerritFileResourceStabilization,
44
- "fetch_util/browser/site_stabilization/gerrit_file_resource_stabilization"
45
- autoload :GithubPullResources, "fetch_util/browser/site_stabilization/github_pull_resources"
46
- autoload :GithubPullResourceStabilization,
47
- "fetch_util/browser/site_stabilization/github_pull_resource_stabilization"
48
- autoload :GithubThreads, "fetch_util/browser/site_stabilization/github_threads"
49
- autoload :GiteaFamilyThreadProductState,
50
- "fetch_util/browser/site_stabilization/gitea_family_thread_product_state"
51
- autoload :GiteaFamilyThreadTimelineState,
52
- "fetch_util/browser/site_stabilization/gitea_family_thread_timeline_state"
53
- autoload :GiteaFamilyStabilization,
54
- "fetch_util/browser/site_stabilization/gitea_family_stabilization"
55
- autoload :GiteaFamilyVisibilityScript,
56
- "fetch_util/browser/site_stabilization/gitea_family_visibility_script"
57
- autoload :GiteaFamilyThreads, "fetch_util/browser/site_stabilization/gitea_family_threads"
58
- autoload :GiteaFamilyPullResourceProductState,
59
- "fetch_util/browser/site_stabilization/gitea_family_pull_resource_product_state"
60
- autoload :GiteaFamilyPullResourceState,
61
- "fetch_util/browser/site_stabilization/gitea_family_pull_resource_state"
62
- autoload :GiteaFamilyPullResources, "fetch_util/browser/site_stabilization/gitea_family_pull_resources"
63
- autoload :GitlabRepo, "fetch_util/browser/site_stabilization/gitlab_repo"
64
- autoload :GitlabMergeRequestResourceVisibilityScript,
65
- "fetch_util/browser/site_stabilization/gitlab_merge_request_resource_visibility_script"
66
- autoload :GitlabMergeRequestResourceStateScript,
67
- "fetch_util/browser/site_stabilization/gitlab_merge_request_resource_state_script"
68
- autoload :GitlabMergeRequestResourceState,
69
- "fetch_util/browser/site_stabilization/gitlab_merge_request_resource_state"
70
- autoload :GitlabMergeRequestResources, "fetch_util/browser/site_stabilization/gitlab_merge_request_resources"
71
- autoload :GitlabStabilization, "fetch_util/browser/site_stabilization/gitlab_stabilization"
72
- autoload :GitlabThreads, "fetch_util/browser/site_stabilization/gitlab_threads"
73
- autoload :SocialPlatforms, "fetch_util/browser/site_stabilization/social_platforms"
74
- autoload :TravelAndLodging, "fetch_util/browser/site_stabilization/travel_and_lodging"
6
+ autoload :CommunityStabilization, "fetch_util/browser/site_stabilization/communities"
7
+ autoload :ForgeStabilization, "fetch_util/browser/site_stabilization/forges"
8
+ autoload :MarketplaceStabilization, "fetch_util/browser/site_stabilization/marketplaces"
9
+ autoload :SocialStabilization, "fetch_util/browser/site_stabilization/social"
10
+ autoload :TravelStabilization, "fetch_util/browser/site_stabilization/travel"
75
11
 
76
- include AzureDevopsPrState
77
- include AzureDevopsPrStabilization
78
- include BitbucketCloudThreadState
79
- include BitbucketCloudThreads
80
- include BitbucketCloudPullResourceModules
81
- include CommunityAndMarketplace
82
- include FacebookStabilization
83
- include GerritChangeState
84
- include GerritChangeStabilization
85
- include GerritFileResourceState
86
- include GerritFileResourceStabilization
87
- include GithubPullResources
88
- include GithubPullResourceStabilization
89
- include GithubThreads
90
- include GiteaFamilyVisibilityScript
91
- include GiteaFamilyThreadProductState
92
- include GiteaFamilyThreadTimelineState
93
- include GiteaFamilyStabilization
94
- include GiteaFamilyThreads
95
- include GiteaFamilyPullResourceProductState
96
- include GiteaFamilyPullResourceState
97
- include GiteaFamilyPullResources
98
- include GitlabRepo
99
- include GitlabStabilization
100
- include GitlabMergeRequestResources
101
- include GitlabThreads
102
- include SocialPlatforms
103
- include TravelAndLodging
12
+ include CommunityStabilization
13
+ include ForgeStabilization
14
+ include MarketplaceStabilization
15
+ include SocialStabilization
16
+ include TravelStabilization
104
17
  end
105
18
  end
106
19
  end
@@ -6,10 +6,10 @@ module FetchUtil
6
6
  module Stabilization
7
7
  module PageFlow
8
8
  PAGE_FLOW_STABILIZATION_PROFILES = [
9
- SiteStabilization::CommunityAndMarketplace::COMMUNITY_MARKETPLACE_STABILIZATION_PROFILES[:stabilize_reddit],
10
- SiteStabilization::SocialPlatforms::SOCIAL_PLATFORM_STABILIZATION_PROFILES[:stabilize_instagram],
11
- SiteStabilization::SocialPlatforms::SOCIAL_PLATFORM_STABILIZATION_PROFILES[:stabilize_facebook],
12
- SiteStabilization::CommunityAndMarketplace::COMMUNITY_MARKETPLACE_STABILIZATION_PROFILES[:stabilize_ebay_search],
9
+ SiteStabilization::RedditStabilization::REDDIT_STABILIZATION_PROFILE,
10
+ SiteStabilization::InstagramStabilization::INSTAGRAM_STABILIZATION_PROFILE,
11
+ SiteStabilization::FacebookStabilization::FACEBOOK_STABILIZATION_PROFILE,
12
+ SiteStabilization::EbayStabilization::EBAY_STABILIZATION_PROFILE,
13
13
  SiteStabilization::GithubThreads::GITHUB_THREAD_STABILIZATION_PROFILE,
14
14
  SiteStabilization::GithubPullResources::GITHUB_PULL_RESOURCE_STABILIZATION_PROFILE,
15
15
  SiteStabilization::GitlabThreads::GITLAB_THREAD_STABILIZATION_PROFILE,
@@ -21,7 +21,7 @@ module FetchUtil
21
21
  SiteStabilization::AzureDevopsPrStabilization::AZURE_DEVOPS_PR_STABILIZATION_PROFILE,
22
22
  SiteStabilization::GerritFileResourceStabilization::GERRIT_FILE_RESOURCE_STABILIZATION_PROFILE,
23
23
  SiteStabilization::GerritChangeStabilization::GERRIT_CHANGE_STABILIZATION_PROFILE,
24
- SiteStabilization::TravelAndLodging::TRAVEL_LODGING_STABILIZATION_PROFILES[:stabilize_lodging_detail],
24
+ SiteStabilization::LodgingStabilization::LODGING_STABILIZATION_PROFILE,
25
25
  { host: "t.me", path_query: ->(uri) { uri.path.match?(%r{\A/s/[^/]+/\d+/?\z}) },
26
26
  strategy: :wait_for_telegram_message, notes: "Wait for the requested public Telegram preview message.",
27
27
  tests: "spec/fetch_util/browser_stabilization_spec.rb" },
@@ -95,6 +95,8 @@ module FetchUtil
95
95
 
96
96
  def matching_stabilization_profile(url, profiles)
97
97
  uri = URI.parse(url)
98
+ return nil unless uri.is_a?(URI::HTTP) && uri.host
99
+
98
100
  host = FetchUtil.strip_www_host(url)
99
101
  profiles.find { |profile| profile_match?(profile, uri, host) }
100
102
  rescue URI::InvalidURIError
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "ferrum"
4
4
  require "json"
5
+ require "securerandom"
5
6
  require "uri"
6
7
 
7
8
  module FetchUtil
@@ -31,6 +32,8 @@ module FetchUtil
31
32
  DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " \
32
33
  "(KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
33
34
  DEFAULT_ACCEPT_LANGUAGE = "en-US,en;q=0.9"
35
+ SHADOW_ROOT_ACCESS_TOKEN = SecureRandom.hex(32).freeze
36
+ SHADOW_ROOT_READER_PROPERTY = "__fetchUtilClosedShadowRootReaderV1_#{SecureRandom.hex(16)}".freeze
34
37
  SOCIAL_LOGIN_PHASE_WAIT = 0.3
35
38
  POST_CONSENT_IDLE_TIMEOUT = 3.0
36
39
  CONTENT_READY_MIN_LENGTH = 200
@@ -230,6 +233,9 @@ module FetchUtil
230
233
  raise unless page_loaded_enough?(page)
231
234
  end
232
235
  stabilize_page(page, loaded_page_url(page, url))
236
+ if page.current_url == "about:blank" && url != "about:blank"
237
+ raise Ferrum::Error, "Request to #{url} finished on about:blank without a public page"
238
+ end
233
239
  page
234
240
  rescue Ferrum::PendingConnectionsError, Ferrum::TimeoutError, Ferrum::Error => e
235
241
  close_page(page)
@@ -5,6 +5,9 @@ require "json"
5
5
  module FetchUtil
6
6
  class Extractor
7
7
  INLINE_ASSET_PATHS = %w[vendor/readability.js vendor/turndown.js extract.js].freeze
8
+ VENDOR_ASSET_PATHS = INLINE_ASSET_PATHS.first(2).freeze
9
+ VENDOR_GLOBAL_NAMES = %w[Readability TurndownService].freeze
10
+ PRIVATE_DELIVERY_SENTINEL = '"fetch-util:standalone-api:v1"'
8
11
  @asset_cache = {}
9
12
  @cache_mutex = Mutex.new
10
13
 
@@ -22,6 +25,7 @@ module FetchUtil
22
25
  @reader_mode = reader_mode
23
26
  @asset_root = (asset_root || File.join(__dir__, "assets")).dup.freeze
24
27
  @extraction_call = nil
28
+ @private_asset_script = nil
25
29
  end
26
30
 
27
31
  def extract(page)
@@ -36,14 +40,26 @@ module FetchUtil
36
40
  private
37
41
 
38
42
  def inject_assets(page)
39
- page.add_script_tag(path: asset_path("vendor/readability.js"))
40
- page.add_script_tag(path: asset_path("vendor/turndown.js"))
41
- page.add_script_tag(path: asset_path("extract.js"))
43
+ INLINE_ASSET_PATHS.each do |relative_path|
44
+ page.add_script_tag(path: asset_path(relative_path))
45
+ end
46
+ end
47
+
48
+ def inject_vendor_assets(page)
49
+ VENDOR_ASSET_PATHS.each do |relative_path|
50
+ page.add_script_tag(path: asset_path(relative_path))
51
+ end
42
52
  end
43
53
 
44
- def inject_assets_inline(page)
45
- inline_asset_scripts.each do |script|
46
- page.evaluate("#{script}\ntrue")
54
+ def inject_vendor_assets_inline(page)
55
+ inline_asset_scripts.first(2).zip(VENDOR_GLOBAL_NAMES).each do |script, global_name|
56
+ page.evaluate(<<~JAVASCRIPT)
57
+ (() => {
58
+ #{script}
59
+ window.#{global_name} = #{global_name};
60
+ return true;
61
+ })()
62
+ JAVASCRIPT
47
63
  end
48
64
  end
49
65
 
@@ -52,14 +68,15 @@ module FetchUtil
52
68
  original_timeout = page.timeout if timeout_supported
53
69
  page.timeout = [original_timeout.to_f, 60].max if timeout_supported
54
70
 
55
- inject_assets(page)
71
+ register_cdp_closed_shadow_roots(page)
72
+ inject_vendor_assets(page)
56
73
  page.evaluate(extraction_call)
57
74
  rescue Ferrum::TimeoutError
58
75
  begin
59
76
  page.evaluate("window.stop && window.stop()")
60
77
  rescue Ferrum::Error
61
78
  end
62
- inject_assets_inline(page)
79
+ inject_vendor_assets_inline(page)
63
80
  page.evaluate(extraction_call)
64
81
  ensure
65
82
  restore_page_timeout(page, original_timeout) if timeout_supported
@@ -71,8 +88,97 @@ module FetchUtil
71
88
  nil
72
89
  end
73
90
 
91
+ def register_cdp_closed_shadow_roots(page)
92
+ return unless page.respond_to?(:command)
93
+
94
+ document = page.command("DOM.getDocument", depth: -1, pierce: true).fetch("root")
95
+ shadow_root_pairs(document).each do |host, root|
96
+ register_cdp_closed_shadow_root(page, host, root)
97
+ end
98
+ rescue Ferrum::Error
99
+ nil
100
+ end
101
+
102
+ def shadow_root_pairs(document)
103
+ pairs = []
104
+ pending = [document]
105
+ until pending.empty?
106
+ node = pending.pop
107
+ shadows = Array(node["shadowRoots"])
108
+ shadows.each { |root| pairs << [node, root] if root["shadowRootType"] == "closed" }
109
+ pending.concat(Array(node["children"]), shadows)
110
+ end
111
+ pairs
112
+ end
113
+
114
+ def register_cdp_closed_shadow_root(page, host, root)
115
+ object_ids = []
116
+ [host, root].each do |node|
117
+ object_ids << page.command(
118
+ "DOM.resolveNode",
119
+ backendNodeId: node.fetch("backendNodeId")
120
+ ).dig("object", "objectId")
121
+ end
122
+ return unless object_ids.all?
123
+
124
+ page.command(
125
+ "Runtime.callFunctionOn",
126
+ objectId: object_ids.first,
127
+ functionDeclaration: closed_shadow_registration_function,
128
+ arguments: [
129
+ { objectId: object_ids.last },
130
+ { value: Browser::SHADOW_ROOT_READER_PROPERTY },
131
+ { value: Browser::SHADOW_ROOT_ACCESS_TOKEN }
132
+ ],
133
+ returnByValue: true
134
+ )
135
+ rescue Ferrum::Error
136
+ nil
137
+ ensure
138
+ Array(object_ids).compact.each do |object_id|
139
+ page.command("Runtime.releaseObject", objectId: object_id)
140
+ rescue Ferrum::Error
141
+ nil
142
+ end
143
+ end
144
+
145
+ def closed_shadow_registration_function
146
+ <<~JAVASCRIPT
147
+ function(root, property, token) {
148
+ const reader = window[property];
149
+ return typeof reader === "function" && reader(token, "set", this, root) === true;
150
+ }
151
+ JAVASCRIPT
152
+ end
153
+
74
154
  def extraction_call
75
- @extraction_call ||= "window.FetchUtilExtract.extract(#{JSON.generate(reader_mode: @reader_mode)})"
155
+ @extraction_call ||= begin
156
+ options = JSON.generate(
157
+ reader_mode: @reader_mode,
158
+ shadow_root_token: Browser::SHADOW_ROOT_ACCESS_TOKEN,
159
+ shadow_root_reader_property: Browser::SHADOW_ROOT_READER_PROPERTY
160
+ )
161
+ <<~JAVASCRIPT
162
+ (() => {
163
+ let __fetchUtilPrivateApi = null;
164
+ const __fetchUtilDeliverExtractApi = api => { __fetchUtilPrivateApi = api; };
165
+ #{private_asset_script}
166
+ if (!__fetchUtilPrivateApi || typeof __fetchUtilPrivateApi.extract !== "function") return null;
167
+ return __fetchUtilPrivateApi.extract(#{options});
168
+ })()
169
+ JAVASCRIPT
170
+ end
171
+ end
172
+
173
+ def private_asset_script
174
+ @private_asset_script ||= begin
175
+ source = inline_asset_scripts.last
176
+ marker_index = source.index(PRIVATE_DELIVERY_SENTINEL)
177
+ duplicate_index = source.index(PRIVATE_DELIVERY_SENTINEL, marker_index.to_i + PRIVATE_DELIVERY_SENTINEL.length)
178
+ raise ExtractionError, "Private extraction delivery sentinel is missing or duplicated" if marker_index.nil? || duplicate_index
179
+
180
+ source.sub(PRIVATE_DELIVERY_SENTINEL, "__fetchUtilDeliverExtractApi").freeze
181
+ end
76
182
  end
77
183
 
78
184
  def inline_asset_scripts
@@ -300,7 +300,7 @@ module FetchUtil
300
300
  def markdown_from_root(root, title)
301
301
  sections = []
302
302
  blocks = root.css(BLOCK_SELECTOR).to_a
303
- blocks.unshift(root) if %w[ul ol].include?(root.name)
303
+ blocks.unshift(root) if BLOCK_ELEMENTS.include?(root.name)
304
304
  blocks.each do |node|
305
305
  next if node.ancestors.any? { |ancestor| %w[ul ol].include?(ancestor.name) }
306
306
 
@@ -320,6 +320,10 @@ module FetchUtil
320
320
  sections << [fence, code, fence].join("\n")
321
321
  when "ul", "ol"
322
322
  sections << markdown_list(node)
323
+ when "li"
324
+ content = node.dup
325
+ content.css("ul, ol").remove
326
+ sections << "- #{clean_text(content.text)}"
323
327
  when "tr"
324
328
  cells = node.css("th, td").map { |cell| clean_text(cell.text) }.reject(&:empty?)
325
329
  sections << "- #{cells.join(": ")}" unless cells.empty?
@@ -3,12 +3,14 @@
3
3
  module FetchUtil
4
4
  class Regulatory
5
5
  module CacheStore
6
+ include CacheValidation
7
+
6
8
  private
7
9
 
8
- def cache_fetch(key)
10
+ def cache_fetch(key, shape: nil)
9
11
  path = cache_file_path(key)
10
12
  cached = read_cache(path)
11
- return cached if cached
13
+ return cached if cached && (!shape || cache_payload_matches?(cached, shape))
12
14
 
13
15
  payload, cacheable = yield
14
16
  begin
@@ -20,7 +22,7 @@ module FetchUtil
20
22
  end
21
23
 
22
24
  def fetch_record(key, uri, fallback: nil, require_success: true)
23
- cache_fetch(key) do
25
+ cache_fetch(key, shape: fallback) do
24
26
  response, cacheable = record_response(uri, require_success: require_success)
25
27
  payload = response ? yield(response.body, response) : fallback
26
28
  [payload, cacheable]
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FetchUtil
4
+ class Regulatory
5
+ module CacheValidation
6
+ private
7
+
8
+ def cache_payload_matches?(payload, shape)
9
+ case shape
10
+ when Hash
11
+ payload.is_a?(Hash) && shape.all? do |key, value_shape|
12
+ payload.key?(key) && cache_payload_matches?(payload[key], value_shape)
13
+ end
14
+ when Array
15
+ payload.is_a?(Array)
16
+ when nil
17
+ payload.nil? || payload.is_a?(String)
18
+ else
19
+ payload.is_a?(shape.class)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -20,7 +20,7 @@ module FetchUtil
20
20
  end
21
21
 
22
22
  def tdm_policy_record(url, target_origin:)
23
- cache_fetch("tdmpolicy:#{origin_key(target_origin)}:#{url}") do
23
+ cache_fetch("tdmpolicy:#{origin_key(target_origin)}:#{url}", shape: { "signals" => [] }) do
24
24
  response, cacheable = safe_get(url)
25
25
  signals = []
26
26
  if response&.status&.between?(200, 299) && json_like_response?(response.headers, response.body)
@@ -43,6 +43,7 @@ module FetchUtil
43
43
  autoload :SourceSelection, "fetch_util/regulatory/source_selection"
44
44
  autoload :Signals, "fetch_util/regulatory/signals"
45
45
  autoload :FetchRecords, "fetch_util/regulatory/fetch_records"
46
+ autoload :CacheValidation, "fetch_util/regulatory/cache_validation"
46
47
  autoload :CacheStore, "fetch_util/regulatory/cache_store"
47
48
  autoload :Robots, "fetch_util/regulatory/robots"
48
49
  autoload :RobotGlobs, "fetch_util/regulatory/robot_globs"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FetchUtil
4
- VERSION = "0.6.2"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fetch_util
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hmdne
@@ -100,54 +100,62 @@ files:
100
100
  - lib/fetch_util/browser/navigation.rb
101
101
  - lib/fetch_util/browser/navigation/headers_and_readiness.rb
102
102
  - lib/fetch_util/browser/navigation/navigator_patch.rb
103
+ - lib/fetch_util/browser/navigation/shadow_root_patch.rb
103
104
  - lib/fetch_util/browser/site_stabilization.rb
104
- - lib/fetch_util/browser/site_stabilization/azure_devops_pr_commit_details_script.rb
105
- - lib/fetch_util/browser/site_stabilization/azure_devops_pr_product_state_script.rb
106
- - lib/fetch_util/browser/site_stabilization/azure_devops_pr_request_helpers_script.rb
107
- - lib/fetch_util/browser/site_stabilization/azure_devops_pr_request_state_script.rb
108
- - lib/fetch_util/browser/site_stabilization/azure_devops_pr_stabilization.rb
109
- - lib/fetch_util/browser/site_stabilization/azure_devops_pr_state.rb
110
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_pull_diff_product_state.rb
111
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_pull_diff_request_state.rb
112
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_pull_diff_state.rb
113
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_pull_resource_modules.rb
114
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_pull_resource_state.rb
115
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_pull_resources.rb
116
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_thread_state.rb
117
- - lib/fetch_util/browser/site_stabilization/bitbucket_cloud_threads.rb
118
- - lib/fetch_util/browser/site_stabilization/community_and_marketplace.rb
119
- - lib/fetch_util/browser/site_stabilization/facebook_stabilization.rb
120
- - lib/fetch_util/browser/site_stabilization/gerrit_change_product_state_script.rb
121
- - lib/fetch_util/browser/site_stabilization/gerrit_change_request_state_script.rb
122
- - lib/fetch_util/browser/site_stabilization/gerrit_change_stabilization.rb
123
- - lib/fetch_util/browser/site_stabilization/gerrit_change_state.rb
124
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_fetch_state_script.rb
125
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_product_state_script.rb
126
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_request_state_script.rb
127
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_route_state_script.rb
128
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_stabilization.rb
129
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_state.rb
130
- - lib/fetch_util/browser/site_stabilization/gerrit_file_resource_validation_state_script.rb
131
- - lib/fetch_util/browser/site_stabilization/gitea_family_pull_resource_product_state.rb
132
- - lib/fetch_util/browser/site_stabilization/gitea_family_pull_resource_state.rb
133
- - lib/fetch_util/browser/site_stabilization/gitea_family_pull_resources.rb
134
- - lib/fetch_util/browser/site_stabilization/gitea_family_stabilization.rb
135
- - lib/fetch_util/browser/site_stabilization/gitea_family_thread_product_state.rb
136
- - lib/fetch_util/browser/site_stabilization/gitea_family_thread_timeline_state.rb
137
- - lib/fetch_util/browser/site_stabilization/gitea_family_threads.rb
138
- - lib/fetch_util/browser/site_stabilization/gitea_family_visibility_script.rb
139
- - lib/fetch_util/browser/site_stabilization/github_pull_resource_stabilization.rb
140
- - lib/fetch_util/browser/site_stabilization/github_pull_resources.rb
141
- - lib/fetch_util/browser/site_stabilization/github_threads.rb
142
- - lib/fetch_util/browser/site_stabilization/gitlab_merge_request_resource_state.rb
143
- - lib/fetch_util/browser/site_stabilization/gitlab_merge_request_resource_state_script.rb
144
- - lib/fetch_util/browser/site_stabilization/gitlab_merge_request_resource_visibility_script.rb
145
- - lib/fetch_util/browser/site_stabilization/gitlab_merge_request_resources.rb
146
- - lib/fetch_util/browser/site_stabilization/gitlab_repo.rb
147
- - lib/fetch_util/browser/site_stabilization/gitlab_stabilization.rb
148
- - lib/fetch_util/browser/site_stabilization/gitlab_threads.rb
149
- - lib/fetch_util/browser/site_stabilization/social_platforms.rb
150
- - lib/fetch_util/browser/site_stabilization/travel_and_lodging.rb
105
+ - lib/fetch_util/browser/site_stabilization/communities.rb
106
+ - lib/fetch_util/browser/site_stabilization/communities/reddit.rb
107
+ - lib/fetch_util/browser/site_stabilization/forges.rb
108
+ - lib/fetch_util/browser/site_stabilization/forges/azure_devops/pull_requests/commit_details_script.rb
109
+ - lib/fetch_util/browser/site_stabilization/forges/azure_devops/pull_requests/product_state_script.rb
110
+ - lib/fetch_util/browser/site_stabilization/forges/azure_devops/pull_requests/request_helpers_script.rb
111
+ - lib/fetch_util/browser/site_stabilization/forges/azure_devops/pull_requests/request_state_script.rb
112
+ - lib/fetch_util/browser/site_stabilization/forges/azure_devops/pull_requests/stabilization.rb
113
+ - lib/fetch_util/browser/site_stabilization/forges/azure_devops/pull_requests/state.rb
114
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/pulls/diff/product_state.rb
115
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/pulls/diff/request_state.rb
116
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/pulls/diff/state.rb
117
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/pulls/resource_modules.rb
118
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/pulls/resource_state.rb
119
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/pulls/resources.rb
120
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/threads/index.rb
121
+ - lib/fetch_util/browser/site_stabilization/forges/bitbucket/threads/state.rb
122
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/changes/product_state_script.rb
123
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/changes/request_state_script.rb
124
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/changes/stabilization.rb
125
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/changes/state.rb
126
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/fetch_state_script.rb
127
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/product_state_script.rb
128
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/request_state_script.rb
129
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/route_state_script.rb
130
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/stabilization.rb
131
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/state.rb
132
+ - lib/fetch_util/browser/site_stabilization/forges/gerrit/resources/validation_state_script.rb
133
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/pulls/product_state.rb
134
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/pulls/resources.rb
135
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/pulls/state.rb
136
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/stabilization.rb
137
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/threads/index.rb
138
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/threads/product_state.rb
139
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/threads/timeline_state.rb
140
+ - lib/fetch_util/browser/site_stabilization/forges/gitea/visibility_script.rb
141
+ - lib/fetch_util/browser/site_stabilization/forges/github/pull_resource_stabilization.rb
142
+ - lib/fetch_util/browser/site_stabilization/forges/github/pull_resources.rb
143
+ - lib/fetch_util/browser/site_stabilization/forges/github/threads.rb
144
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/merge_requests/resource_state.rb
145
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/merge_requests/resource_state_script.rb
146
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/merge_requests/resource_visibility_script.rb
147
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/merge_requests/resources.rb
148
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/repository.rb
149
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/stabilization.rb
150
+ - lib/fetch_util/browser/site_stabilization/forges/gitlab/threads.rb
151
+ - lib/fetch_util/browser/site_stabilization/forges/observation_state.rb
152
+ - lib/fetch_util/browser/site_stabilization/marketplaces.rb
153
+ - lib/fetch_util/browser/site_stabilization/marketplaces/ebay.rb
154
+ - lib/fetch_util/browser/site_stabilization/social.rb
155
+ - lib/fetch_util/browser/site_stabilization/social/facebook.rb
156
+ - lib/fetch_util/browser/site_stabilization/social/instagram.rb
157
+ - lib/fetch_util/browser/site_stabilization/travel.rb
158
+ - lib/fetch_util/browser/site_stabilization/travel/lodging.rb
151
159
  - lib/fetch_util/browser/stabilization.rb
152
160
  - lib/fetch_util/browser/stabilization/anubis.rb
153
161
  - lib/fetch_util/browser/stabilization/page_flow.rb
@@ -160,6 +168,7 @@ files:
160
168
  - lib/fetch_util/raw_docs_fallback.rb
161
169
  - lib/fetch_util/regulatory.rb
162
170
  - lib/fetch_util/regulatory/cache_store.rb
171
+ - lib/fetch_util/regulatory/cache_validation.rb
163
172
  - lib/fetch_util/regulatory/directives.rb
164
173
  - lib/fetch_util/regulatory/fetch_records.rb
165
174
  - lib/fetch_util/regulatory/headers.rb