html2rss 0.30.0 → 0.32.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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/lib/html2rss/article/deduplicator.rb +2 -4
  4. data/lib/html2rss/article.rb +16 -39
  5. data/lib/html2rss/auto_source/README.md +12 -9
  6. data/lib/html2rss/auto_source/cleanup.rb +34 -7
  7. data/lib/html2rss/auto_source/scraper/html.rb +5 -0
  8. data/lib/html2rss/auto_source/scraper/json_document.rb +135 -0
  9. data/lib/html2rss/auto_source/scraper/json_state/article_normalizer.rb +74 -11
  10. data/lib/html2rss/auto_source/scraper/json_state/candidate_detector.rb +4 -8
  11. data/lib/html2rss/auto_source/scraper/json_state.rb +5 -0
  12. data/lib/html2rss/auto_source/scraper/meta_oembed.rb +8 -1
  13. data/lib/html2rss/auto_source/scraper/microdata.rb +8 -0
  14. data/lib/html2rss/auto_source/scraper/microformats2.rb +8 -0
  15. data/lib/html2rss/auto_source/scraper/native_feed.rb +3 -0
  16. data/lib/html2rss/auto_source/scraper/schema/thing.rb +4 -5
  17. data/lib/html2rss/auto_source/scraper/schema.rb +8 -0
  18. data/lib/html2rss/auto_source/scraper/semantic_html.rb +5 -0
  19. data/lib/html2rss/auto_source/scraper/sitemap.rb +3 -0
  20. data/lib/html2rss/auto_source/scraper/wordpress_api.rb +3 -0
  21. data/lib/html2rss/auto_source/scraper.rb +12 -17
  22. data/lib/html2rss/auto_source/segment.rb +7 -4
  23. data/lib/html2rss/auto_source/segmenter/cluster.rb +22 -23
  24. data/lib/html2rss/auto_source/segmenter/list.rb +54 -31
  25. data/lib/html2rss/auto_source/segmenter.rb +5 -5
  26. data/lib/html2rss/auto_source.rb +58 -3
  27. data/lib/html2rss/batch.rb +3 -1
  28. data/lib/html2rss/capture/README.md +6 -4
  29. data/lib/html2rss/capture.rb +67 -35
  30. data/lib/html2rss/channel.rb +3 -3
  31. data/lib/html2rss/cli/probe_view.rb +5 -3
  32. data/lib/html2rss/cli/render.rb +6 -5
  33. data/lib/html2rss/cli/validate.rb +28 -27
  34. data/lib/html2rss/cli.rb +6 -2
  35. data/lib/html2rss/config/auto_source_contract.rb +3 -0
  36. data/lib/html2rss/config/issue_mapper.rb +108 -0
  37. data/lib/html2rss/config/request_headers.rb +2 -3
  38. data/lib/html2rss/config/schema.rb +16 -1
  39. data/lib/html2rss/config/selectors_validator.rb +92 -70
  40. data/lib/html2rss/config/validation_issue.rb +60 -0
  41. data/lib/html2rss/config/validation_report.rb +55 -0
  42. data/lib/html2rss/config/validator.rb +10 -6
  43. data/lib/html2rss/config.rb +44 -114
  44. data/lib/html2rss/defaults.rb +42 -30
  45. data/lib/html2rss/doctor/botasaurus.rb +6 -4
  46. data/lib/html2rss/doctor.rb +8 -0
  47. data/lib/html2rss/error.rb +1 -1
  48. data/lib/html2rss/feed_builder/rss.rb +9 -14
  49. data/lib/html2rss/feed_pipeline/auto_fallback.rb +11 -2
  50. data/lib/html2rss/feed_pipeline/runtime_policy.rb +14 -10
  51. data/lib/html2rss/feed_pipeline/strategy_plan.rb +6 -2
  52. data/lib/html2rss/feed_resolution/candidate_generator.rb +1 -4
  53. data/lib/html2rss/feed_resolution/diag.rb +3 -1
  54. data/lib/html2rss/feed_resolution/options.rb +3 -1
  55. data/lib/html2rss/feed_resolution/policy.rb +2 -2
  56. data/lib/html2rss/feed_resolution.rb +30 -2
  57. data/lib/html2rss/html/article_extractor/date_extractor.rb +1 -1
  58. data/lib/html2rss/html/article_extractor.rb +2 -4
  59. data/lib/html2rss/html/feed_link.rb +3 -1
  60. data/lib/html2rss/html/navigator/text_extractor.rb +1 -1
  61. data/lib/html2rss/html/navigator.rb +10 -10
  62. data/lib/html2rss/html/rendering/description_builder.rb +1 -1
  63. data/lib/html2rss/html/sst_article_extractor.rb +8 -5
  64. data/lib/html2rss/link_destination/destination_facts.rb +4 -2
  65. data/lib/html2rss/link_destination/noise_policy.rb +10 -1
  66. data/lib/html2rss/link_destination/path_classifier.rb +45 -38
  67. data/lib/html2rss/mcp/README.md +2 -0
  68. data/lib/html2rss/mcp/config_argument.rb +3 -1
  69. data/lib/html2rss/mcp/contract.rb +7 -21
  70. data/lib/html2rss/mcp/outcome/playbook.rb +2 -2
  71. data/lib/html2rss/mcp/outcome.rb +36 -43
  72. data/lib/html2rss/mcp/server/tools.rb +111 -131
  73. data/lib/html2rss/mcp/server.rb +26 -18
  74. data/lib/html2rss/page_recon/README.md +5 -5
  75. data/lib/html2rss/page_recon/diagnostics.rb +71 -81
  76. data/lib/html2rss/page_recon.rb +9 -5
  77. data/lib/html2rss/recon.rb +19 -30
  78. data/lib/html2rss/request_service/botasaurus_contract.rb +11 -12
  79. data/lib/html2rss/request_service/botasaurus_strategy.rb +1 -1
  80. data/lib/html2rss/request_service/compressed_body.rb +4 -1
  81. data/lib/html2rss/request_service/httpx_strategy.rb +39 -28
  82. data/lib/html2rss/request_service/network_guard.rb +7 -5
  83. data/lib/html2rss/request_service/response.rb +6 -4
  84. data/lib/html2rss/request_service/strategy.rb +3 -8
  85. data/lib/html2rss/request_service.rb +11 -2
  86. data/lib/html2rss/scoring/container_assessor.rb +3 -1
  87. data/lib/html2rss/scoring/engine.rb +8 -8
  88. data/lib/html2rss/scoring/observation.rb +5 -3
  89. data/lib/html2rss/scoring/ranked_segment.rb +3 -1
  90. data/lib/html2rss/scrape_target.rb +3 -1
  91. data/lib/html2rss/selector_candidates.rb +234 -0
  92. data/lib/html2rss/selectors/README.md +64 -0
  93. data/lib/html2rss/selectors/extractors/attribute.rb +13 -15
  94. data/lib/html2rss/selectors/extractors/href.rb +15 -16
  95. data/lib/html2rss/selectors/extractors/html.rb +8 -9
  96. data/lib/html2rss/selectors/extractors/static.rb +12 -14
  97. data/lib/html2rss/selectors/extractors/text.rb +8 -9
  98. data/lib/html2rss/selectors/extractors.rb +33 -20
  99. data/lib/html2rss/selectors/item_env.rb +31 -0
  100. data/lib/html2rss/selectors/{object_to_xml_converter.rb → json_xml.rb} +11 -12
  101. data/lib/html2rss/selectors/option_spec.rb +140 -0
  102. data/lib/html2rss/selectors/post_processors/base.rb +79 -31
  103. data/lib/html2rss/selectors/post_processors/gsub.rb +103 -39
  104. data/lib/html2rss/selectors/post_processors/html_transformers/transform_urls_to_absolute_ones.rb +4 -4
  105. data/lib/html2rss/selectors/post_processors/markdown_to_html.rb +10 -11
  106. data/lib/html2rss/selectors/post_processors/parse_time.rb +50 -26
  107. data/lib/html2rss/selectors/post_processors/parse_uri.rb +11 -7
  108. data/lib/html2rss/selectors/post_processors/sanitize_html.rb +46 -32
  109. data/lib/html2rss/selectors/post_processors/substring.rb +14 -25
  110. data/lib/html2rss/selectors/post_processors/template.rb +23 -19
  111. data/lib/html2rss/selectors/post_processors.rb +6 -5
  112. data/lib/html2rss/selectors/{schema_doc.rb → schema_export.rb} +12 -31
  113. data/lib/html2rss/selectors/step_env.rb +24 -0
  114. data/lib/html2rss/selectors.rb +231 -142
  115. data/lib/html2rss/sst/attrs.rb +5 -3
  116. data/lib/html2rss/sst/document.rb +3 -1
  117. data/lib/html2rss/sst/index.rb +3 -1
  118. data/lib/html2rss/sst/node.rb +29 -10
  119. data/lib/html2rss/sst/normalizer.rb +12 -16
  120. data/lib/html2rss/sst/tags.rb +7 -7
  121. data/lib/html2rss/status.rb +13 -8
  122. data/lib/html2rss/surface_category.rb +14 -32
  123. data/lib/html2rss/syndication/parser.rb +2 -2
  124. data/lib/html2rss/test/enhance_audit.rb +1 -3
  125. data/lib/html2rss/test/policy.rb +106 -0
  126. data/lib/html2rss/test.rb +126 -173
  127. data/lib/html2rss/url.rb +25 -4
  128. data/lib/html2rss/version.rb +1 -1
  129. data/lib/html2rss.rb +32 -19
  130. data/lib/tasks/config_schema.rake +2 -2
  131. data/schema/html2rss-config.schema.json +26 -8
  132. metadata +16 -6
  133. data/lib/html2rss/selectors/item_scope.rb +0 -30
@@ -281,6 +281,18 @@
281
281
  },
282
282
  "required": []
283
283
  },
284
+ "json_document": {
285
+ "type": "object",
286
+ "properties": {
287
+ "enabled": {
288
+ "type": "boolean",
289
+ "not": {
290
+ "type": "null"
291
+ }
292
+ }
293
+ },
294
+ "required": []
295
+ },
284
296
  "xhr_articles": {
285
297
  "type": "object",
286
298
  "properties": {
@@ -402,6 +414,9 @@
402
414
  "json_state": {
403
415
  "enabled": true
404
416
  },
417
+ "json_document": {
418
+ "enabled": true
419
+ },
405
420
  "xhr_articles": {
406
421
  "enabled": true
407
422
  },
@@ -434,7 +449,8 @@
434
449
  "properties": {
435
450
  "selector": {
436
451
  "type": "string",
437
- "minLength": 1
452
+ "minLength": 1,
453
+ "default": "a[href]"
438
454
  },
439
455
  "order": {
440
456
  "enum": [
@@ -507,9 +523,7 @@
507
523
  ]
508
524
  }
509
525
  },
510
- "required": [
511
- "selector"
512
- ],
526
+ "required": [],
513
527
  "description": "Defines the items selector and list-card enhance settings."
514
528
  },
515
529
  "enclosure": {
@@ -848,7 +862,7 @@
848
862
  "gsub": {
849
863
  "type": "object",
850
864
  "title": "gsub",
851
- "description": "Replace matches of `pattern` in the extracted string with `replacement` (Ruby String#gsub; pattern may be a regexp-like string).",
865
+ "description": "Replace matches of `pattern` in the extracted string with `replacement` (Ruby String#gsub; pattern may be a regexp-like string). Patterns over 256 characters as written, or with nested quantifiers, are rejected.",
852
866
  "examples": [
853
867
  {
854
868
  "name": "gsub",
@@ -862,10 +876,14 @@
862
876
  "const": "gsub"
863
877
  },
864
878
  "pattern": {
865
- "type": "string"
879
+ "type": "string",
880
+ "maxLength": 256
866
881
  },
867
882
  "replacement": {
868
- "type": "string"
883
+ "type": [
884
+ "string",
885
+ "object"
886
+ ]
869
887
  }
870
888
  },
871
889
  "required": [
@@ -1024,7 +1042,7 @@
1024
1042
  "type": "string",
1025
1043
  "const": "href",
1026
1044
  "title": "href",
1027
- "description": "Return the absolute URL from the selected element's `href` attribute (relative hrefs are resolved against the channel URL).",
1045
+ "description": "Return the absolute URL from the selected element's `href` attribute (relative hrefs are resolved against the page base URL).",
1028
1046
  "examples": [
1029
1047
  "href"
1030
1048
  ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html2rss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gil Desmarais
@@ -275,6 +275,7 @@ files:
275
275
  - lib/html2rss/auto_source/cleanup.rb
276
276
  - lib/html2rss/auto_source/scraper.rb
277
277
  - lib/html2rss/auto_source/scraper/html.rb
278
+ - lib/html2rss/auto_source/scraper/json_document.rb
278
279
  - lib/html2rss/auto_source/scraper/json_state.rb
279
280
  - lib/html2rss/auto_source/scraper/json_state/article_normalizer.rb
280
281
  - lib/html2rss/auto_source/scraper/json_state/candidate_detector.rb
@@ -314,13 +315,17 @@ files:
314
315
  - lib/html2rss/config.rb
315
316
  - lib/html2rss/config/auto_source_contract.rb
316
317
  - lib/html2rss/config/dynamic_params.rb
318
+ - lib/html2rss/config/issue_mapper.rb
317
319
  - lib/html2rss/config/multiple_feeds_config.rb
318
320
  - lib/html2rss/config/request_controls.rb
319
321
  - lib/html2rss/config/request_headers.rb
320
322
  - lib/html2rss/config/schema.rb
321
323
  - lib/html2rss/config/selectors_validator.rb
324
+ - lib/html2rss/config/validation_issue.rb
325
+ - lib/html2rss/config/validation_report.rb
322
326
  - lib/html2rss/config/validator.rb
323
327
  - lib/html2rss/defaults.rb
328
+ - lib/html2rss/doctor.rb
324
329
  - lib/html2rss/doctor/botasaurus.rb
325
330
  - lib/html2rss/error.rb
326
331
  - lib/html2rss/feed_builder.rb
@@ -421,15 +426,18 @@ files:
421
426
  - lib/html2rss/scoring/ranked_segment.rb
422
427
  - lib/html2rss/scoring/score.rb
423
428
  - lib/html2rss/scrape_target.rb
429
+ - lib/html2rss/selector_candidates.rb
424
430
  - lib/html2rss/selectors.rb
431
+ - lib/html2rss/selectors/README.md
425
432
  - lib/html2rss/selectors/extractors.rb
426
433
  - lib/html2rss/selectors/extractors/attribute.rb
427
434
  - lib/html2rss/selectors/extractors/href.rb
428
435
  - lib/html2rss/selectors/extractors/html.rb
429
436
  - lib/html2rss/selectors/extractors/static.rb
430
437
  - lib/html2rss/selectors/extractors/text.rb
431
- - lib/html2rss/selectors/item_scope.rb
432
- - lib/html2rss/selectors/object_to_xml_converter.rb
438
+ - lib/html2rss/selectors/item_env.rb
439
+ - lib/html2rss/selectors/json_xml.rb
440
+ - lib/html2rss/selectors/option_spec.rb
433
441
  - lib/html2rss/selectors/post_processors.rb
434
442
  - lib/html2rss/selectors/post_processors/base.rb
435
443
  - lib/html2rss/selectors/post_processors/gsub.rb
@@ -441,7 +449,8 @@ files:
441
449
  - lib/html2rss/selectors/post_processors/sanitize_html.rb
442
450
  - lib/html2rss/selectors/post_processors/substring.rb
443
451
  - lib/html2rss/selectors/post_processors/template.rb
444
- - lib/html2rss/selectors/schema_doc.rb
452
+ - lib/html2rss/selectors/schema_export.rb
453
+ - lib/html2rss/selectors/step_env.rb
445
454
  - lib/html2rss/sst.rb
446
455
  - lib/html2rss/sst/attrs.rb
447
456
  - lib/html2rss/sst/document.rb
@@ -459,6 +468,7 @@ files:
459
468
  - lib/html2rss/syndication/parser.rb
460
469
  - lib/html2rss/test.rb
461
470
  - lib/html2rss/test/enhance_audit.rb
471
+ - lib/html2rss/test/policy.rb
462
472
  - lib/html2rss/url.rb
463
473
  - lib/html2rss/version.rb
464
474
  - lib/tasks/config_schema.rake
@@ -468,7 +478,7 @@ licenses:
468
478
  - MIT
469
479
  metadata:
470
480
  allowed_push_host: https://rubygems.org
471
- changelog_uri: https://github.com/html2rss/html2rss/releases/tag/v0.30.0
481
+ changelog_uri: https://github.com/html2rss/html2rss/releases/tag/v0.32.0
472
482
  rubygems_mfa_required: 'true'
473
483
  rdoc_options: []
474
484
  require_paths:
@@ -484,7 +494,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
484
494
  - !ruby/object:Gem::Version
485
495
  version: '0'
486
496
  requirements: []
487
- rubygems_version: 4.0.16
497
+ rubygems_version: 4.0.20
488
498
  specification_version: 4
489
499
  summary: Generates RSS feeds from websites by scraping a URL and using CSS selectors
490
500
  to extract item.
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Html2rss
4
- class Selectors
5
- ##
6
- # Per-item extraction scope: owns the article node and page +base_url+ for one
7
- # extraction pass. Channel hashes are injected (and cached) by {Selectors}.
8
- #
9
- # Distinct from {Context}, which is the post-processor invocation bag (+options+).
10
- # Post-processor +config+ is derived from {#channel} — not a parallel bag.
11
- ItemScope = Data.define(:item, :base_url, :scraper, :channel) do
12
- ##
13
- # Selects an attribute using this scope's item and base_url.
14
- #
15
- # @param name [Symbol, String]
16
- # @return [Object, Array<Object>]
17
- def select(name) = scraper.select_in_scope(name, self)
18
-
19
- ##
20
- # Builds a post-processor {Context} carrying this scope for nested selects.
21
- #
22
- # @param options [Hash] post-processor options from the selector config
23
- # @option options [String] :name post-processor name
24
- # @return [Context]
25
- def context_for(options:)
26
- Context.new(options:, item:, config: { channel: }, scraper:, item_scope: self)
27
- end
28
- end
29
- end
30
- end