graph-crawler 5.3.3__tar.gz → 5.3.4__tar.gz

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 (481) hide show
  1. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/CHANGELOG.md +33 -0
  2. {graph_crawler-5.3.3/graph_crawler.egg-info → graph_crawler-5.3.4}/PKG-INFO +56 -65
  3. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/README.md +55 -64
  4. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/node_scanner.py +5 -5
  5. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/spider.py +72 -36
  6. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/node.py +80 -4
  7. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/__init__.py +2 -0
  8. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/control_channel.py +27 -17
  9. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/isolated_executor.py +7 -0
  10. {graph_crawler-5.3.3 → graph_crawler-5.3.4/graph_crawler.egg-info}/PKG-INFO +56 -65
  11. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/pyproject.toml +1 -1
  12. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/LICENSE +0 -0
  13. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/MANIFEST.in +0 -0
  14. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/__init__.py +0 -0
  15. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/__version__.py +0 -0
  16. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/ai/__init__.py +0 -0
  17. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/__init__.py +0 -0
  18. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/_core.py +0 -0
  19. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/_distributed.py +0 -0
  20. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/_shared.py +0 -0
  21. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/_sitemap_distributed.py +0 -0
  22. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/async_.py +0 -0
  23. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/cli.py +0 -0
  24. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/client/__init__.py +0 -0
  25. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/client/client.py +0 -0
  26. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/history_manager.py +0 -0
  27. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/project_init.py +0 -0
  28. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/server/__init__.py +0 -0
  29. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/server/crawl_monitor.py +0 -0
  30. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/server/dashboard.py +0 -0
  31. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/server/rest_api.py +0 -0
  32. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/server/webhooks.py +0 -0
  33. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/server/websocket_manager.py +0 -0
  34. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/stats_collector.py +0 -0
  35. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/api/sync.py +0 -0
  36. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/__init__.py +0 -0
  37. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/bootstrap.py +0 -0
  38. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/context/__init__.py +0 -0
  39. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/context/dependency_registry.py +0 -0
  40. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/context/graph_context.py +0 -0
  41. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/context/merge_context.py +0 -0
  42. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/__init__.py +0 -0
  43. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/edge_dto.py +0 -0
  44. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/graph_dto.py +0 -0
  45. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/mappers/__init__.py +0 -0
  46. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/mappers/edge_mapper.py +0 -0
  47. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/mappers/graph_mapper.py +0 -0
  48. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/mappers/node_data_codec.py +0 -0
  49. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/mappers/node_mapper.py +0 -0
  50. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/node_dto.py +0 -0
  51. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/dto/utils.py +0 -0
  52. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/__init__.py +0 -0
  53. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/application_container.py +0 -0
  54. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/driver_factory.py +0 -0
  55. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/__init__.py +0 -0
  56. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/base_exporter.py +0 -0
  57. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/csv_exporter.py +0 -0
  58. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/edge_exporter.py +0 -0
  59. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/excel_exporter.py +0 -0
  60. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/jsonl_exporter.py +0 -0
  61. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/node_exporter.py +0 -0
  62. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/parquet_exporter.py +0 -0
  63. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/exporters/sql_exporter.py +0 -0
  64. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/finalize.py +0 -0
  65. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/low_memory_graph_saver.py +0 -0
  66. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/resource_budget.py +0 -0
  67. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/scheduling/__init__.py +0 -0
  68. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/scheduling/rule_based_scheduler.py +0 -0
  69. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/services/storage_factory.py +0 -0
  70. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/__init__.py +0 -0
  71. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/__init__.py +0 -0
  72. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/adaptive_throttler.py +0 -0
  73. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/async_bridge.py +0 -0
  74. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/base_spider.py +0 -0
  75. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/celery_batch_spider.py +0 -0
  76. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/celery_spider.py +0 -0
  77. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/checkpoint.py +0 -0
  78. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/crawl_coordinator.py +0 -0
  79. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/dead_letter_queue.py +0 -0
  80. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/domain_rate_limiter.py +0 -0
  81. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/filters/__init__.py +0 -0
  82. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/filters/base.py +0 -0
  83. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/filters/domain_filter.py +0 -0
  84. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/filters/domain_patterns.py +0 -0
  85. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/filters/path_filter.py +0 -0
  86. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/incremental_strategy.py +0 -0
  87. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/link_processor.py +0 -0
  88. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/multiprocess_spider.py +0 -0
  89. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/parsers/__init__.py +0 -0
  90. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/parsers/base.py +0 -0
  91. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/parsers/html_parser.py +0 -0
  92. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/progress_tracker.py +0 -0
  93. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/scheduler.py +0 -0
  94. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/serialization_mixin.py +0 -0
  95. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/sitemap_parser.py +0 -0
  96. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/sitemap_processor.py +0 -0
  97. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/sitemap_spider.py +0 -0
  98. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/spider_lifecycle.py +0 -0
  99. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/crawling/worker_pool.py +0 -0
  100. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/graph_export.py +0 -0
  101. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/graph_merge/__init__.py +0 -0
  102. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/graph_merge/graph_operations.py +0 -0
  103. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/application/use_cases/graph_merge/merge_strategies.py +0 -0
  104. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/data/__init__.py +0 -0
  105. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/data/backends/__init__.py +0 -0
  106. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/data/backends/memory.py +0 -0
  107. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/data/backends/sqlite.py +0 -0
  108. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/data/interfaces.py +0 -0
  109. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/__init__.py +0 -0
  110. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/context/__init__.py +0 -0
  111. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/context/crawl_context.py +0 -0
  112. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/__init__.py +0 -0
  113. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/edge.py +0 -0
  114. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/edge_analysis.py +0 -0
  115. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/graph.py +0 -0
  116. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/graph_operations.py +0 -0
  117. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/graph_statistics.py +0 -0
  118. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/merge_strategies.py +0 -0
  119. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/redirect_mode.py +0 -0
  120. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/registries.py +0 -0
  121. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/sitemap_node.py +0 -0
  122. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/entities/strategies.py +0 -0
  123. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/__init__.py +0 -0
  124. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/base.py +0 -0
  125. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/crawl.py +0 -0
  126. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/edge.py +0 -0
  127. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/event_bus.py +0 -0
  128. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/events.py +0 -0
  129. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/fetch.py +0 -0
  130. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/idempotent_subscriber.py +0 -0
  131. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/legacy_type_map.py +0 -0
  132. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/llm.py +0 -0
  133. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/node.py +0 -0
  134. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/plugin.py +0 -0
  135. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/scheduler.py +0 -0
  136. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/events/storage.py +0 -0
  137. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/adapter.py +0 -0
  138. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/crawl_context.py +0 -0
  139. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/distributed_spider.py +0 -0
  140. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/driver.py +0 -0
  141. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/event_bus.py +0 -0
  142. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/exporters.py +0 -0
  143. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/filter.py +0 -0
  144. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/merge_context.py +0 -0
  145. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/node_interfaces.py +0 -0
  146. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/parser.py +0 -0
  147. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/plugin_manager.py +0 -0
  148. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/processor.py +0 -0
  149. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/scanner.py +0 -0
  150. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/scheduler.py +0 -0
  151. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/scheduling/__init__.py +0 -0
  152. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/scheduling/frontier.py +0 -0
  153. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/scheduling/scheduler.py +0 -0
  154. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/spider.py +0 -0
  155. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/stop_condition.py +0 -0
  156. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/storage/__init__.py +0 -0
  157. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/storage/base.py +0 -0
  158. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/storage/eviction.py +0 -0
  159. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/interfaces/storage/queue.py +0 -0
  160. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/__init__.py +0 -0
  161. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/adjacency_maintenance.py +0 -0
  162. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/cycle_detection.py +0 -0
  163. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/degree_ranking.py +0 -0
  164. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/depth_context.py +0 -0
  165. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/display_title.py +0 -0
  166. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/duplicate_detection.py +0 -0
  167. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/duplicate_selector.py +0 -0
  168. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/duplicate_stats.py +0 -0
  169. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/edge_filter.py +0 -0
  170. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/edge_statistics.py +0 -0
  171. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/eviction_policy.py +0 -0
  172. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/graph_query_cascade.py +0 -0
  173. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/graph_statistics.py +0 -0
  174. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/graph_traversal.py +0 -0
  175. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/language_versions.py +0 -0
  176. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/lsh_index.py +0 -0
  177. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/minimap_builder.py +0 -0
  178. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/node_content_resolver.py +0 -0
  179. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/node_view_builder.py +0 -0
  180. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/pagination_guards.py +0 -0
  181. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/predicate_ops.py +0 -0
  182. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/schema_filter.py +0 -0
  183. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/search_haystack.py +0 -0
  184. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/simhash_clustering.py +0 -0
  185. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/simhash_fallback.py +0 -0
  186. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/similarity_ranking.py +0 -0
  187. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/text_search.py +0 -0
  188. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/url_clustering.py +0 -0
  189. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/url_deduplication.py +0 -0
  190. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/url_normalizer.py +0 -0
  191. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/url_rule_matcher.py +0 -0
  192. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/services/url_tree_builder.py +0 -0
  193. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/__init__.py +0 -0
  194. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/configs.py +0 -0
  195. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/domain_patterns.py +0 -0
  196. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/fetch_options.py +0 -0
  197. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/html_document.py +0 -0
  198. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/lifecycle.py +0 -0
  199. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/models.py +0 -0
  200. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/page_actions.py +0 -0
  201. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/redirect_hop.py +0 -0
  202. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/scheduling/__init__.py +0 -0
  203. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/scheduling/crawl_request.py +0 -0
  204. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/scheduling/retry_policy.py +0 -0
  205. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/settings.py +0 -0
  206. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/url_normalization.py +0 -0
  207. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/domain/value_objects/views.py +0 -0
  208. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/__init__.py +0 -0
  209. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/__init__.py +0 -0
  210. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/base.py +0 -0
  211. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/cache_middleware.py +0 -0
  212. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/chain.py +0 -0
  213. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/error_recovery_middleware.py +0 -0
  214. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/logging_middleware.py +0 -0
  215. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/proxy_health.py +0 -0
  216. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/proxy_middleware.py +0 -0
  217. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/proxy_models.py +0 -0
  218. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/proxy_selection.py +0 -0
  219. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/rate_limit_middleware.py +0 -0
  220. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/request_response_middleware.py +0 -0
  221. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/retry_middleware.py +0 -0
  222. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/robots_cache.py +0 -0
  223. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/robots_middleware.py +0 -0
  224. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/robots_validator.py +0 -0
  225. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/middleware/user_agent_middleware.py +0 -0
  226. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/__init__.py +0 -0
  227. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/builtin/__init__.py +0 -0
  228. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/builtin/stats_export_plugin.py +0 -0
  229. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/crawl_engine/__init__.py +0 -0
  230. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/crawl_engine/base.py +0 -0
  231. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/crawl_engine/priority_provider.py +0 -0
  232. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/crawl_engine/smart_crawl.py +0 -0
  233. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/__init__.py +0 -0
  234. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/anti_bot_detection.py +0 -0
  235. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/anti_bot_playwright.py +0 -0
  236. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/anti_bot_scripts.py +0 -0
  237. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/anti_bot_stealth.py +0 -0
  238. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/base.py +0 -0
  239. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/captcha/__init__.py +0 -0
  240. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/captcha/detector.py +0 -0
  241. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/captcha/models.py +0 -0
  242. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/captcha/plugin.py +0 -0
  243. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/engine/captcha/services.py +0 -0
  244. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/__init__.py +0 -0
  245. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/base.py +0 -0
  246. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/content_extractors/__init__.py +0 -0
  247. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/content_extractors/base.py +0 -0
  248. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/content_extractors/goose3_extractor.py +0 -0
  249. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/content_extractors/newspaper_extractor.py +0 -0
  250. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/content_extractors/plugin.py +0 -0
  251. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/content_extractors/readability_extractor.py +0 -0
  252. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/defaults.py +0 -0
  253. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/extractors/__init__.py +0 -0
  254. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/extractors/email_extractor.py +0 -0
  255. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/extractors/phone_extractor.py +0 -0
  256. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/extractors/price_extractor.py +0 -0
  257. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/links.py +0 -0
  258. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/markdown_plugin.py +0 -0
  259. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/metadata.py +0 -0
  260. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/smart_page_finder.py +0 -0
  261. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/__init__.py +0 -0
  262. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/constants.py +0 -0
  263. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/exceptions.py +0 -0
  264. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/extractor.py +0 -0
  265. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/options.py +0 -0
  266. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/__init__.py +0 -0
  267. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/base.py +0 -0
  268. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/jsonld.py +0 -0
  269. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/microdata.py +0 -0
  270. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/opengraph.py +0 -0
  271. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/rdfa.py +0 -0
  272. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/parsers/twitter.py +0 -0
  273. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/plugin.py +0 -0
  274. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/structured_data/result.py +0 -0
  275. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/text.py +0 -0
  276. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/node/url_normalizer.py +0 -0
  277. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/extensions/plugins/registry.py +0 -0
  278. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/__init__.py +0 -0
  279. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/adapters/__init__.py +0 -0
  280. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/adapters/base.py +0 -0
  281. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/adapters/selectolax_adapter.py +0 -0
  282. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/__init__.py +0 -0
  283. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/celery_app.py +0 -0
  284. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/celery_batch.py +0 -0
  285. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/celery_job_task.py +0 -0
  286. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/celery_unified.py +0 -0
  287. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/config.py +0 -0
  288. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/easy_crawler.py +0 -0
  289. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/messaging/worker_api.py +0 -0
  290. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/__init__.py +0 -0
  291. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/auto_storage.py +0 -0
  292. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/base.py +0 -0
  293. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/eviction.py +0 -0
  294. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/graph_repository.py +0 -0
  295. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/json/__init__.py +0 -0
  296. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/json_storage.py +0 -0
  297. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/lmdb_eviction_storage.py +0 -0
  298. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/memory/__init__.py +0 -0
  299. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/memory_dedup_store.py +0 -0
  300. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/memory_frontier.py +0 -0
  301. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/memory_politeness_store.py +0 -0
  302. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/memory_storage.py +0 -0
  303. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/mongodb_storage.py +0 -0
  304. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/naming_strategy.py +0 -0
  305. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/postgresql_storage.py +0 -0
  306. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/protocols/__init__.py +0 -0
  307. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/redis_dedup_store.py +0 -0
  308. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/redis_politeness_store.py +0 -0
  309. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/repository.py +0 -0
  310. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/sqlite/__init__.py +0 -0
  311. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/sqlite_eviction_storage.py +0 -0
  312. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/sqlite_storage.py +0 -0
  313. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/__init__.py +0 -0
  314. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/file_job_storage.py +0 -0
  315. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/file_queue_storage.py +0 -0
  316. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/memory_job_storage.py +0 -0
  317. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/memory_queue_storage.py +0 -0
  318. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/postgresql_job_storage.py +0 -0
  319. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/postgresql_queue_storage.py +0 -0
  320. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/persistence/unified/unified_storage.py +0 -0
  321. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/__init__.py +0 -0
  322. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/__init__.py +0 -0
  323. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/config.py +0 -0
  324. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/context.py +0 -0
  325. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/driver.py +0 -0
  326. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/driver_curl_cffi.py +0 -0
  327. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/__init__.py +0 -0
  328. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/autothrottle.py +0 -0
  329. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/autothrottle_adaptive.py +0 -0
  330. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/headers.py +0 -0
  331. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/http_cache.py +0 -0
  332. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/rate_limiter.py +0 -0
  333. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/plugins/retry.py +0 -0
  334. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/async_http/stages.py +0 -0
  335. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/base.py +0 -0
  336. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/base_plugin.py +0 -0
  337. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/compression/__init__.py +0 -0
  338. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/compression/strategy.py +0 -0
  339. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/connection_pool.py +0 -0
  340. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/context.py +0 -0
  341. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/core/__init__.py +0 -0
  342. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/core/base_async.py +0 -0
  343. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/core/body_decoder.py +0 -0
  344. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/core/mixins.py +0 -0
  345. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/core/redirect_utils.py +0 -0
  346. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/escalation.py +0 -0
  347. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/factory.py +0 -0
  348. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/__init__.py +0 -0
  349. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/adaptive_semaphore.py +0 -0
  350. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/batch_scheduler.py +0 -0
  351. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/browser_factory.py +0 -0
  352. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/browser_profile.py +0 -0
  353. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/config.py +0 -0
  354. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/context.py +0 -0
  355. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/driver.py +0 -0
  356. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/engine.py +0 -0
  357. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/fetch_metrics.py +0 -0
  358. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/net/__init__.py +0 -0
  359. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/net/geoip.py +0 -0
  360. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/net/proxy_pool.py +0 -0
  361. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/net/rate_limiter.py +0 -0
  362. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/page_actions.py +0 -0
  363. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/page_fetch.py +0 -0
  364. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/__init__.py +0 -0
  365. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/_network_record.py +0 -0
  366. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/captcha_detector.py +0 -0
  367. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/captcha_solver.py +0 -0
  368. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/cookie_consent.py +0 -0
  369. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/form_filler.py +0 -0
  370. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/human_bezier.py +0 -0
  371. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/network_log.py +0 -0
  372. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/ram_adaptive.py +0 -0
  373. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/screenshot.py +0 -0
  374. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/plugins/user_profile.py +0 -0
  375. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/pool_lifecycle.py +0 -0
  376. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/pooled_driver.py +0 -0
  377. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/stages.py +0 -0
  378. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/playwright/stealth_hints.py +0 -0
  379. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/plugin_manager.py +0 -0
  380. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/protocols.py +0 -0
  381. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/undetected/__init__.py +0 -0
  382. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/infrastructure/transport/undetected/async_driver.py +0 -0
  383. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/native/__init__.py +0 -0
  384. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/native/bloom_filter.pyx +0 -0
  385. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/native/html_parser.pyx +0 -0
  386. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/native/setup.py +0 -0
  387. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/native/url_utils.pyx +0 -0
  388. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/__init__.py +0 -0
  389. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/decorators/__init__.py +0 -0
  390. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/decorators/cache.py +0 -0
  391. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/decorators/log.py +0 -0
  392. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/decorators/retry.py +0 -0
  393. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/decorators/timing.py +0 -0
  394. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/error_tracing.py +0 -0
  395. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/extensions/__init__.py +0 -0
  396. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/extensions/memory_guard.py +0 -0
  397. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/extensions/state_manager.py +0 -0
  398. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/__init__.py +0 -0
  399. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/base.py +0 -0
  400. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/base_metrics_listener.py +0 -0
  401. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/crawl_listener.py +0 -0
  402. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/dlq_listener.py +0 -0
  403. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/error_listener.py +0 -0
  404. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/logging_listener.py +0 -0
  405. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/metrics_listener.py +0 -0
  406. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/node_listener.py +0 -0
  407. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/plugin_listener.py +0 -0
  408. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/storage_listener.py +0 -0
  409. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/listeners/url_listener.py +0 -0
  410. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/metrics/__init__.py +0 -0
  411. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/metrics/memory_profiler.py +0 -0
  412. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/metrics/metrics_collector.py +0 -0
  413. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/metrics/prometheus_metrics.py +0 -0
  414. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/metrics_core.py +0 -0
  415. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/structured_logging.py +0 -0
  416. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/observability/tracing.py +0 -0
  417. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/optimizations/__init__.py +0 -0
  418. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/optimizations/generators.py +0 -0
  419. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/optimizations/memory_slots.py +0 -0
  420. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/optimizations/simhash_numba.py +0 -0
  421. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/py.typed +0 -0
  422. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/__init__.py +0 -0
  423. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/constants.py +0 -0
  424. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/dto/__init__.py +0 -0
  425. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/dto/edge_dto.py +0 -0
  426. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/dto/graph_dto.py +0 -0
  427. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/dto/node_dto.py +0 -0
  428. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/error_handling/__init__.py +0 -0
  429. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/error_handling/error_handler.py +0 -0
  430. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/exceptions.py +0 -0
  431. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/platform/__init__.py +0 -0
  432. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/platform/adaptive_resolver.py +0 -0
  433. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/platform/config.py +0 -0
  434. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/platform/detector.py +0 -0
  435. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/platform/diagnostics.py +0 -0
  436. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/platform/metrics.py +0 -0
  437. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/security/__init__.py +0 -0
  438. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/security/url_sanitizer.py +0 -0
  439. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/security/url_validator.py +0 -0
  440. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/__init__.py +0 -0
  441. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/async_compat.py +0 -0
  442. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/bloom_filter.py +0 -0
  443. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/captcha/__init__.py +0 -0
  444. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/captcha/base.py +0 -0
  445. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/captcha/manager.py +0 -0
  446. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/celery_config.py +0 -0
  447. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/celery_helpers.py +0 -0
  448. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/cgroup_limits.py +0 -0
  449. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/content_decoder.py +0 -0
  450. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/distributed_rate_limiter.py +0 -0
  451. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/distributed_rate_limiter_backends.py +0 -0
  452. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/dns_cache.py +0 -0
  453. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/enterprise_ua_generator.py +0 -0
  454. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/event_publisher_mixin.py +0 -0
  455. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/fast_json.py +0 -0
  456. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/fingerprint.py +0 -0
  457. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/fingerprint_data.py +0 -0
  458. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/fingerprint_generators.py +0 -0
  459. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/fingerprint_profile.py +0 -0
  460. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/fingerprint_stealth.py +0 -0
  461. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/html_utils.py +0 -0
  462. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/id_generator.py +0 -0
  463. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/memory_optimizer.py +0 -0
  464. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/rate_limiter.py +0 -0
  465. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/regex_validator.py +0 -0
  466. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/shadow_dom.py +0 -0
  467. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/text_utils.py +0 -0
  468. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/url_patterns.py +0 -0
  469. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/url_utils.py +0 -0
  470. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/user_agent_rotator.py +0 -0
  471. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/validation_helpers.py +0 -0
  472. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/visualization.py +0 -0
  473. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/shared/utils/visualization_core.py +0 -0
  474. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/tools/__init__.py +0 -0
  475. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler/tools/db_cleanup.py +0 -0
  476. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler.egg-info/SOURCES.txt +0 -0
  477. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler.egg-info/dependency_links.txt +0 -0
  478. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler.egg-info/entry_points.txt +0 -0
  479. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler.egg-info/requires.txt +0 -0
  480. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/graph_crawler.egg-info/top_level.txt +0 -0
  481. {graph_crawler-5.3.3 → graph_crawler-5.3.4}/setup.cfg +0 -0
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.3.4 — 2026-10
4
+
5
+ ### Fixed
6
+
7
+ - **The per-node enrichment limit measures active work, not the HTML-pool queue.** `NodeScanner` wraps
8
+ `enrich_node` in `enrich_budget(seconds)` (`graph_crawler.domain.entities.node`): while a call waits for a thread
9
+ of the HTML executor in `run_in_html_pool` (parse, `_update_from_context`, hashes), the deadline is suspended and
10
+ resumed with the same remainder the moment the work starts. With 5.3.3 the hook and the hashes moved into the
11
+ shared pool, so under load a node's wall-clock depended on how many neighbours stood in front of it: a burst of
12
+ large pages produced a cascade of `Per-node timeout … marking as ERROR` for pages whose own processing took a
13
+ fraction of the limit (and every stage re-queued behind the others). A page whose **own** processing exceeds
14
+ `node_enrich_timeout` still times out and still returns its discovered links. The crawl-level timeout remains the
15
+ upper bound for a node that waits in the queue.
16
+ - **`IsolatedExecutor.run` admits at most `max_workers` tasks at a time** (`threading.BoundedSemaphore`): the task
17
+ `timeout` now measures execution in the child process, not the wait behind other tasks. Previously a burst of
18
+ submissions could expire the timeout of a queued task, which killed the whole pool and failed every task in flight.
19
+
20
+ - **Control commands are no longer dropped silently.** `GraphSpider.process_control_messages` executes only
21
+ `SPIDER_COMMANDS` (`STOP`, `PAUSE`, `RESUME`, `REPRIORITIZE`, `FORCE_VISIT`); any other `CrawlCommand` logs
22
+ `WARNING Unsupported control command '<value>' from <sender> is ignored (supported: ...)` once per command type
23
+ per crawl (repeats at `DEBUG`) and the crawl goes on. `REPRIORITIZE` without `url`/`priority` and `FORCE_VISIT`
24
+ without `url` log a `WARNING` with the received `data` instead of doing nothing.
25
+
26
+ ### Added
27
+
28
+ - `EnrichBudget` (`pause()` / `resume()`) and `enrich_budget()` in `graph_crawler.domain.entities.node`.
29
+ - `SPIDER_COMMANDS` (`graph_crawler.domain.interfaces`): the commands `GraphSpider` executes.
30
+
31
+ ### Deprecated
32
+
33
+ - `CrawlCommand.SKIP_URL`, `CHANGE_STRATEGY`, `SET_DEPTH_LIMIT`, `REQUEST_STATE`, `UPDATE_GOAL` were never executed
34
+ by the spider. They will be removed from the enum in 5.4.0.
35
+
3
36
  ## 5.3.3 — 2026-10
4
37
 
5
38
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graph-crawler
3
- Version: 5.3.3
3
+ Version: 5.3.4
4
4
  Summary: Web crawler that builds a graph of a site. HTTP → TLS → browser escalation with pluggable browser engines.
5
5
  Author: 0-EternalJunior-0
6
6
  Maintainer: 0-EternalJunior-0
@@ -139,34 +139,20 @@ pip install "graph-crawler[browser]" # + browser (stock Playwright)
139
139
  pip install "graph-crawler[antidetect]" # + TLS, patchright, CloakBrowser, nodriver
140
140
  ```
141
141
 
142
- <<<<<<< HEAD
143
- | extra | what it adds |
144
- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
145
- | `browser` | `driver="playwright"` with bundled Chromium (`playwright` = alias) |
146
- | `tls` | `curl_cffi` — TLS fingerprint tier (`driver="tls"`) |
147
- | `stealth` | `patchright` + `playwright-stealth` — engine `"patchright"` |
148
- | `cloak` | `cloakbrowser` — engine `"cloakbrowser"` (stealth in the binary) |
149
- | `undetected` | `nodriver` — last tier (`driver="nodriver"`) |
150
- | `antidetect` | all four above |
151
- | `server` | FastAPI dashboard / REST / WebSocket (`graph_crawler.api.server`) |
152
- | `text` | `tiktoken` + `stop-words` — `count_tokens`, `extract_keywords` |
153
- | `markdown` | `html2md-clean` + `markdown-it-py` — `MarkdownPlugin` |
154
- | `mongodb`, `postgresql`, `celery`, `viz`, `articles`, `fuzzy`, `performance`, `system`, `all` | storage backends, distributed mode, extras |
155
- =======
156
- | extra | what it adds |
157
- |--------------|---------------------------------------------------------------------|
158
- | `browser` | `driver="playwright"` (`playwright` is an alias of this extra) |
159
- | `tls` | `curl_cffi` — TLS fingerprint tier, `driver="tls"` |
160
- | `stealth` | `patchright` + `playwright-stealth` — engine `"patchright"` |
161
- | `cloak` | `cloakbrowser` — engine `"cloakbrowser"` (stealth in the binary) |
162
- | `undetected` | `nodriver` — last tier, `driver="nodriver"` |
163
- | `antidetect` | the four extras above |
164
- | `server` | FastAPI dashboard / REST / WebSocket |
165
- | `text` | `tiktoken` + `stop-words` |
166
- | `markdown` | `html2md-clean` + `markdown-it-py` — `MarkdownPlugin` |
167
- | `fuzzy` | `rapidfuzz` — `graph.search(..., mode="fuzzy")` |
168
- | `mongodb`, `postgresql`, `celery`, `viz`, `articles`, `sitemap-fast`, `performance`, `system`, `all` | storage backends, distributed mode and other extras |
169
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
142
+ | extra | what it adds |
143
+ | -------------- | ------------------------------------------------------------------------------- |
144
+ | `browser` | `driver="playwright"`, stock Playwright (`playwright` is an alias of this extra) |
145
+ | `tls` | `curl_cffi` — TLS fingerprint tier, `driver="tls"` |
146
+ | `stealth` | `patchright` + `playwright-stealth` — engine `"patchright"` |
147
+ | `cloak` | `cloakbrowser` — engine `"cloakbrowser"` (stealth in the binary) |
148
+ | `undetected` | `nodriver` — last tier, `driver="nodriver"` |
149
+ | `antidetect` | the five extras above |
150
+ | `server` | FastAPI dashboard / REST / WebSocket (`graph_crawler.api.server`) |
151
+ | `text` | `tiktoken` + `stop-words` — `count_tokens`, `extract_keywords` |
152
+ | `markdown` | `html2md-clean` + `markdown-it-py` — `MarkdownPlugin` |
153
+ | `fuzzy` | `rapidfuzz` — `graph.search(..., mode="fuzzy")` |
154
+ | `sitemap-fast` | `lxml` sitemap parser |
155
+ | `mongodb`, `postgresql`, `celery`, `viz`, `articles`, `performance`, `system`, `all` | storage backends, distributed mode and other extras |
170
156
 
171
157
  After installing a browser extra, download the browser once:
172
158
 
@@ -267,23 +253,13 @@ so `node.ok` is `False` for error pages.
267
253
 
268
254
  `driver=` accepts a name or a driver instance.
269
255
 
270
- <<<<<<< HEAD
271
- | name | tier | needs | notes |
272
- | ------------------- | ----------- | --------------------- | ----------------------------------------------- |
273
- | `http` / `async` | http | — | aiohttp, default |
274
- | `tls` / `curl_cffi` | tls | `[tls]` | Chrome TLS fingerprint, no JS |
275
- | `playwright` | browser | `[playwright]` | real browser; engine chosen by `browser_engine` |
276
- | `nodriver` | undetected | `[undetected]` | CDP without webdriver, async |
277
- | `escalating` | all of them | whatever is installed | http → tls → browser → undetected |
278
- =======
279
- | name | tier | needs | notes |
280
- |--------------|-------------|-----------------------|-------------------------------------------------|
281
- | `aiohttp` / `http` | http | — | default |
282
- | `tls` | tls | `[tls]` | Chrome TLS fingerprint, no JavaScript |
283
- | `playwright` | browser | `[browser]` | real browser; engine chosen by `browser_engine` |
284
- | `nodriver` | undetected | `[undetected]` | CDP without webdriver |
285
- | `escalating` | all of them | whatever is installed | http → tls → browser → undetected |
286
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
256
+ | name | tier | needs | notes |
257
+ | ----------------------------- | ----------- | --------------------- | ----------------------------------------------- |
258
+ | `aiohttp` / `http` / `async` | http | — | default |
259
+ | `tls` / `curl_cffi` | tls | `[tls]` | Chrome TLS fingerprint, no JavaScript |
260
+ | `playwright` | browser | `[browser]` | real browser; engine chosen by `browser_engine` |
261
+ | `nodriver` | undetected | `[undetected]` | CDP without webdriver |
262
+ | `escalating` | all of them | whatever is installed | http → tls → browser → undetected |
287
263
 
288
264
  `escalating` creates tiers lazily: a browser starts only when a cheaper tier is
289
265
  blocked. Tiers whose dependency is not installed are skipped.
@@ -299,13 +275,8 @@ Every response goes through `classify_block()`:
299
275
  | `none` | normal page |
300
276
  | `soft_block` | 429, plain 503, or 200 with an empty body |
301
277
  | `challenge` | Cloudflare "Just a moment", `cf-mitigated: challenge`, Incapsula, DDoS-Guard |
302
- <<<<<<< HEAD
303
- | `hard_block` | 403 "Access Denied", Akamai `_abck`, DataDome headers |
304
- | `captcha` | PerimeterX, DataDome captcha, reCAPTCHA / hCaptcha / Turnstile markup |
305
- =======
306
278
  | `hard_block` | 403/401/405 **with** a vendor or "Access Denied" marker (Akamai `_abck`, DataDome/Incapsula headers, `x-iinfo`, Cloudflare `server`) |
307
- | `captcha` | PerimeterX, DataDome captcha, reCAPTCHA / hCaptcha / Turnstile markup |
308
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
279
+ | `captcha` | PerimeterX, DataDome captcha, reCAPTCHA / hCaptcha / Turnstile markup |
309
280
 
310
281
  Anything other than `none`, and network errors, move the request to the next tier.
311
282
 
@@ -375,25 +346,14 @@ policy = EscalationPolicy(
375
346
 
376
347
  ### Browser engines
377
348
 
378
- <<<<<<< HEAD
379
- The browser tier is stock `PlaywrightDriver`; the _engine_ decides which
380
- Playwright module and which Chromium binary it launches.
381
-
382
- | engine | what it is | falls back to |
383
- | -------------- | ------------------------------------------------------------- | ------------- |
384
- | `cloakbrowser` | Chromium with fingerprint patches compiled into the binary | `patchright` |
385
- | `patchright` | Playwright fork without automation leaks (`Runtime.enable` …) | `playwright` |
386
- | `playwright` | stock | — |
387
- =======
388
- The browser tier is `PlaywrightDriver`; the *engine* decides which Playwright
349
+ The browser tier is `PlaywrightDriver`; the _engine_ decides which Playwright
389
350
  module and which Chromium binary it launches.
390
351
 
391
352
  | engine | what it is | falls back to |
392
- |----------------|---------------------------------------------------------------|---------------|
353
+ | -------------- | ------------------------------------------------------------- | ------------- |
393
354
  | `cloakbrowser` | Chromium with fingerprint patches compiled into the binary | `patchright` |
394
355
  | `patchright` | Playwright fork without automation leaks (`Runtime.enable` …) | `playwright` |
395
356
  | `playwright` | stock Playwright | — |
396
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
397
357
 
398
358
  A fallback is logged once at `INFO` with the reason. To get an `ImportError`
399
359
  instead of a silent downgrade, set `GC_ENGINE_STRICT=1`, or pass
@@ -456,6 +416,37 @@ A target is found by `selector`, `text`, `placeholder` or `label` (`nth` picks
456
416
  among visible matches). The first failing action stops the chain and is written
457
417
  to `node.metadata["actions_error"]`.
458
418
 
419
+ ## Controlling a running crawl
420
+
421
+ `async_crawl(..., control_channel=channel)` (and `crawl`, `GraphCrawlerClient.crawl`)
422
+ reads commands from the channel between pages:
423
+
424
+ ```python
425
+ import graph_crawler as gc
426
+ from graph_crawler.domain.interfaces import AsyncQueueControlChannel, force_visit_command, stop_command
427
+
428
+ channel = AsyncQueueControlChannel()
429
+ channel.send(force_visit_command("https://example.com/hidden-page"))
430
+ graph = await gc.async_crawl("https://example.com", control_channel=channel)
431
+
432
+ # from a plugin, a callback or another task while the crawl runs:
433
+ channel.send(stop_command("enough data")) # graph.metadata["stop_reason"] == "enough data"
434
+ ```
435
+
436
+ | command | `data` | effect |
437
+ | -------------- | ----------------------- | ---------------------------------------------- |
438
+ | `STOP` | `reason` (optional) | stops the crawl, `stop_reason` = `reason` |
439
+ | `PAUSE` | — | pauses until `RESUME` or `STOP` |
440
+ | `RESUME` | — | continues a paused crawl |
441
+ | `REPRIORITIZE` | `url`, `priority` | moves a queued URL in the queue |
442
+ | `FORCE_VISIT` | `url` | adds the URL to the queue with top priority |
443
+
444
+ `SPIDER_COMMANDS` is this set. `SKIP_URL`, `CHANGE_STRATEGY`, `SET_DEPTH_LIMIT`,
445
+ `REQUEST_STATE` and `UPDATE_GOAL` are declared in `CrawlCommand` but not executed:
446
+ the spider logs `WARNING Unsupported control command ...` (once per command type
447
+ per crawl) and goes on; they will be removed in the next minor release. A supported
448
+ command without its required `data` fields is also skipped with a `WARNING`.
449
+
459
450
  ## Plugins and custom nodes
460
451
 
461
452
  A node plugin runs on every fetched page and writes to `user_data`, `metadata`
@@ -18,34 +18,20 @@ pip install "graph-crawler[browser]" # + browser (stock Playwright)
18
18
  pip install "graph-crawler[antidetect]" # + TLS, patchright, CloakBrowser, nodriver
19
19
  ```
20
20
 
21
- <<<<<<< HEAD
22
- | extra | what it adds |
23
- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
24
- | `browser` | `driver="playwright"` with bundled Chromium (`playwright` = alias) |
25
- | `tls` | `curl_cffi` — TLS fingerprint tier (`driver="tls"`) |
26
- | `stealth` | `patchright` + `playwright-stealth` — engine `"patchright"` |
27
- | `cloak` | `cloakbrowser` — engine `"cloakbrowser"` (stealth in the binary) |
28
- | `undetected` | `nodriver` — last tier (`driver="nodriver"`) |
29
- | `antidetect` | all four above |
30
- | `server` | FastAPI dashboard / REST / WebSocket (`graph_crawler.api.server`) |
31
- | `text` | `tiktoken` + `stop-words` — `count_tokens`, `extract_keywords` |
32
- | `markdown` | `html2md-clean` + `markdown-it-py` — `MarkdownPlugin` |
33
- | `mongodb`, `postgresql`, `celery`, `viz`, `articles`, `fuzzy`, `performance`, `system`, `all` | storage backends, distributed mode, extras |
34
- =======
35
- | extra | what it adds |
36
- |--------------|---------------------------------------------------------------------|
37
- | `browser` | `driver="playwright"` (`playwright` is an alias of this extra) |
38
- | `tls` | `curl_cffi` — TLS fingerprint tier, `driver="tls"` |
39
- | `stealth` | `patchright` + `playwright-stealth` — engine `"patchright"` |
40
- | `cloak` | `cloakbrowser` — engine `"cloakbrowser"` (stealth in the binary) |
41
- | `undetected` | `nodriver` — last tier, `driver="nodriver"` |
42
- | `antidetect` | the four extras above |
43
- | `server` | FastAPI dashboard / REST / WebSocket |
44
- | `text` | `tiktoken` + `stop-words` |
45
- | `markdown` | `html2md-clean` + `markdown-it-py` — `MarkdownPlugin` |
46
- | `fuzzy` | `rapidfuzz` — `graph.search(..., mode="fuzzy")` |
47
- | `mongodb`, `postgresql`, `celery`, `viz`, `articles`, `sitemap-fast`, `performance`, `system`, `all` | storage backends, distributed mode and other extras |
48
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
21
+ | extra | what it adds |
22
+ | -------------- | ------------------------------------------------------------------------------- |
23
+ | `browser` | `driver="playwright"`, stock Playwright (`playwright` is an alias of this extra) |
24
+ | `tls` | `curl_cffi` — TLS fingerprint tier, `driver="tls"` |
25
+ | `stealth` | `patchright` + `playwright-stealth` — engine `"patchright"` |
26
+ | `cloak` | `cloakbrowser` — engine `"cloakbrowser"` (stealth in the binary) |
27
+ | `undetected` | `nodriver` — last tier, `driver="nodriver"` |
28
+ | `antidetect` | the five extras above |
29
+ | `server` | FastAPI dashboard / REST / WebSocket (`graph_crawler.api.server`) |
30
+ | `text` | `tiktoken` + `stop-words` — `count_tokens`, `extract_keywords` |
31
+ | `markdown` | `html2md-clean` + `markdown-it-py` — `MarkdownPlugin` |
32
+ | `fuzzy` | `rapidfuzz` — `graph.search(..., mode="fuzzy")` |
33
+ | `sitemap-fast` | `lxml` sitemap parser |
34
+ | `mongodb`, `postgresql`, `celery`, `viz`, `articles`, `performance`, `system`, `all` | storage backends, distributed mode and other extras |
49
35
 
50
36
  After installing a browser extra, download the browser once:
51
37
 
@@ -146,23 +132,13 @@ so `node.ok` is `False` for error pages.
146
132
 
147
133
  `driver=` accepts a name or a driver instance.
148
134
 
149
- <<<<<<< HEAD
150
- | name | tier | needs | notes |
151
- | ------------------- | ----------- | --------------------- | ----------------------------------------------- |
152
- | `http` / `async` | http | — | aiohttp, default |
153
- | `tls` / `curl_cffi` | tls | `[tls]` | Chrome TLS fingerprint, no JS |
154
- | `playwright` | browser | `[playwright]` | real browser; engine chosen by `browser_engine` |
155
- | `nodriver` | undetected | `[undetected]` | CDP without webdriver, async |
156
- | `escalating` | all of them | whatever is installed | http → tls → browser → undetected |
157
- =======
158
- | name | tier | needs | notes |
159
- |--------------|-------------|-----------------------|-------------------------------------------------|
160
- | `aiohttp` / `http` | http | — | default |
161
- | `tls` | tls | `[tls]` | Chrome TLS fingerprint, no JavaScript |
162
- | `playwright` | browser | `[browser]` | real browser; engine chosen by `browser_engine` |
163
- | `nodriver` | undetected | `[undetected]` | CDP without webdriver |
164
- | `escalating` | all of them | whatever is installed | http → tls → browser → undetected |
165
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
135
+ | name | tier | needs | notes |
136
+ | ----------------------------- | ----------- | --------------------- | ----------------------------------------------- |
137
+ | `aiohttp` / `http` / `async` | http | — | default |
138
+ | `tls` / `curl_cffi` | tls | `[tls]` | Chrome TLS fingerprint, no JavaScript |
139
+ | `playwright` | browser | `[browser]` | real browser; engine chosen by `browser_engine` |
140
+ | `nodriver` | undetected | `[undetected]` | CDP without webdriver |
141
+ | `escalating` | all of them | whatever is installed | http → tls → browser → undetected |
166
142
 
167
143
  `escalating` creates tiers lazily: a browser starts only when a cheaper tier is
168
144
  blocked. Tiers whose dependency is not installed are skipped.
@@ -178,13 +154,8 @@ Every response goes through `classify_block()`:
178
154
  | `none` | normal page |
179
155
  | `soft_block` | 429, plain 503, or 200 with an empty body |
180
156
  | `challenge` | Cloudflare "Just a moment", `cf-mitigated: challenge`, Incapsula, DDoS-Guard |
181
- <<<<<<< HEAD
182
- | `hard_block` | 403 "Access Denied", Akamai `_abck`, DataDome headers |
183
- | `captcha` | PerimeterX, DataDome captcha, reCAPTCHA / hCaptcha / Turnstile markup |
184
- =======
185
157
  | `hard_block` | 403/401/405 **with** a vendor or "Access Denied" marker (Akamai `_abck`, DataDome/Incapsula headers, `x-iinfo`, Cloudflare `server`) |
186
- | `captcha` | PerimeterX, DataDome captcha, reCAPTCHA / hCaptcha / Turnstile markup |
187
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
158
+ | `captcha` | PerimeterX, DataDome captcha, reCAPTCHA / hCaptcha / Turnstile markup |
188
159
 
189
160
  Anything other than `none`, and network errors, move the request to the next tier.
190
161
 
@@ -254,25 +225,14 @@ policy = EscalationPolicy(
254
225
 
255
226
  ### Browser engines
256
227
 
257
- <<<<<<< HEAD
258
- The browser tier is stock `PlaywrightDriver`; the _engine_ decides which
259
- Playwright module and which Chromium binary it launches.
260
-
261
- | engine | what it is | falls back to |
262
- | -------------- | ------------------------------------------------------------- | ------------- |
263
- | `cloakbrowser` | Chromium with fingerprint patches compiled into the binary | `patchright` |
264
- | `patchright` | Playwright fork without automation leaks (`Runtime.enable` …) | `playwright` |
265
- | `playwright` | stock | — |
266
- =======
267
- The browser tier is `PlaywrightDriver`; the *engine* decides which Playwright
228
+ The browser tier is `PlaywrightDriver`; the _engine_ decides which Playwright
268
229
  module and which Chromium binary it launches.
269
230
 
270
231
  | engine | what it is | falls back to |
271
- |----------------|---------------------------------------------------------------|---------------|
232
+ | -------------- | ------------------------------------------------------------- | ------------- |
272
233
  | `cloakbrowser` | Chromium with fingerprint patches compiled into the binary | `patchright` |
273
234
  | `patchright` | Playwright fork without automation leaks (`Runtime.enable` …) | `playwright` |
274
235
  | `playwright` | stock Playwright | — |
275
- >>>>>>> d3db85cd9c8826d11c10e33988c837f3e8ffbe88
276
236
 
277
237
  A fallback is logged once at `INFO` with the reason. To get an `ImportError`
278
238
  instead of a silent downgrade, set `GC_ENGINE_STRICT=1`, or pass
@@ -335,6 +295,37 @@ A target is found by `selector`, `text`, `placeholder` or `label` (`nth` picks
335
295
  among visible matches). The first failing action stops the chain and is written
336
296
  to `node.metadata["actions_error"]`.
337
297
 
298
+ ## Controlling a running crawl
299
+
300
+ `async_crawl(..., control_channel=channel)` (and `crawl`, `GraphCrawlerClient.crawl`)
301
+ reads commands from the channel between pages:
302
+
303
+ ```python
304
+ import graph_crawler as gc
305
+ from graph_crawler.domain.interfaces import AsyncQueueControlChannel, force_visit_command, stop_command
306
+
307
+ channel = AsyncQueueControlChannel()
308
+ channel.send(force_visit_command("https://example.com/hidden-page"))
309
+ graph = await gc.async_crawl("https://example.com", control_channel=channel)
310
+
311
+ # from a plugin, a callback or another task while the crawl runs:
312
+ channel.send(stop_command("enough data")) # graph.metadata["stop_reason"] == "enough data"
313
+ ```
314
+
315
+ | command | `data` | effect |
316
+ | -------------- | ----------------------- | ---------------------------------------------- |
317
+ | `STOP` | `reason` (optional) | stops the crawl, `stop_reason` = `reason` |
318
+ | `PAUSE` | — | pauses until `RESUME` or `STOP` |
319
+ | `RESUME` | — | continues a paused crawl |
320
+ | `REPRIORITIZE` | `url`, `priority` | moves a queued URL in the queue |
321
+ | `FORCE_VISIT` | `url` | adds the URL to the queue with top priority |
322
+
323
+ `SPIDER_COMMANDS` is this set. `SKIP_URL`, `CHANGE_STRATEGY`, `SET_DEPTH_LIMIT`,
324
+ `REQUEST_STATE` and `UPDATE_GOAL` are declared in `CrawlCommand` but not executed:
325
+ the spider logs `WARNING Unsupported control command ...` (once per command type
326
+ per crawl) and goes on; they will be removed in the next minor release. A supported
327
+ command without its required `data` fields is also skipped with a `WARNING`.
328
+
338
329
  ## Plugins and custom nodes
339
330
 
340
331
  A node plugin runs on every fetched page and writes to `user_data`, `metadata`
@@ -13,7 +13,7 @@ import re
13
13
  from typing import Any
14
14
  from urllib.parse import urlsplit
15
15
 
16
- from graph_crawler.domain.entities.node import Node
16
+ from graph_crawler.domain.entities.node import Node, enrich_budget
17
17
  from graph_crawler.domain.interfaces.driver import IDriver
18
18
  from graph_crawler.domain.interfaces.node_interfaces import IRetainsRawResponse
19
19
  from graph_crawler.domain.value_objects.lifecycle import NodeLifecycle
@@ -627,9 +627,9 @@ class NodeScanner:
627
627
  if result is not None and result.html is not None:
628
628
  html = result.html
629
629
  try:
630
- # asyncio.timeout замість wait_for: у Python 3.11 wait_for губить
631
- # зовнішнє скасування (Ctrl+C), якщо enrich завершився одночасно з ним.
632
- async with asyncio.timeout(self.enrich_timeout):
630
+ # enrich_budget (над asyncio.timeout, не wait_for: у Python 3.11 wait_for
631
+ # губить зовнішнє скасування). Черга html-пулу в ліміт не входить.
632
+ async with enrich_budget(self.enrich_timeout):
633
633
  links = await node.enrich_node(
634
634
  html,
635
635
  crawl_context=crawl_context,
@@ -799,7 +799,7 @@ class NodeScanner:
799
799
  loop = asyncio.get_running_loop()
800
800
  started = loop.time()
801
801
  try:
802
- async with asyncio.timeout(self.enrich_timeout):
802
+ async with enrich_budget(self.enrich_timeout):
803
803
  return await enrich_single_node(node, result)
804
804
  except TimeoutError:
805
805
  return (node, self._on_enrich_timeout(node), None)
@@ -41,7 +41,9 @@ from graph_crawler.domain.entities.graph import Graph
41
41
  from graph_crawler.domain.entities.node import Node
42
42
  from graph_crawler.domain.events import CrawlerEvent, EventBus, EventType
43
43
  from graph_crawler.domain.interfaces.control_channel import (
44
+ SPIDER_COMMANDS,
44
45
  AsyncQueueControlChannel,
46
+ ControlMessage,
45
47
  CrawlCommand,
46
48
  IControlChannel,
47
49
  )
@@ -104,6 +106,7 @@ class GraphSpider(BaseSpider):
104
106
  self._stop_mode = stop_mode
105
107
  self._stop_reason: str | None = None
106
108
  self._control_channel = control_channel or AsyncQueueControlChannel()
109
+ self._warned_unsupported_commands: set[CrawlCommand] = set()
107
110
 
108
111
  # Domain-граф — приватне поле, назовні не витікає (публічний API — DTO).
109
112
  if graph is not None:
@@ -745,52 +748,85 @@ class GraphSpider(BaseSpider):
745
748
  return self._control_channel
746
749
 
747
750
  def process_control_messages(self) -> str | None:
748
- """
749
- Обробити всі команди з control channel.
751
+ """Виконує всі команди з control channel; повертає причину зупинки, якщо прийшов ``STOP``.
750
752
 
751
- Returns:
752
- Причина зупинки якщо отримано команду STOP, інакше None
753
+ Команда поза ``SPIDER_COMMANDS`` або без обов'язкових полів у ``data`` не виконується
754
+ і дає WARNING (непідтримувана — раз на тип команди за краул).
753
755
  """
754
756
  while self._control_channel.has_pending():
755
757
  msg = self._control_channel.receive()
756
758
  if msg is None:
757
759
  break
758
-
759
760
  logger.debug("Processing control message: %s from %s", msg.command.value, msg.sender)
761
+ if msg.command not in SPIDER_COMMANDS:
762
+ self._warn_unsupported_command(msg)
763
+ continue
764
+ stop_reason = self._execute_command(msg)
765
+ if stop_reason is not None:
766
+ return stop_reason
767
+ return None
760
768
 
761
- if msg.command == CrawlCommand.STOP:
762
- reason = msg.data.get("reason", "Stop requested via control channel")
763
- logger.info("Stop command received: %s", reason)
764
- return reason
765
-
766
- elif msg.command == CrawlCommand.PAUSE:
767
- self._state = CrawlerState.PAUSED
768
- logger.info("Pause command received")
769
-
770
- elif msg.command == CrawlCommand.RESUME:
771
- if self._state == CrawlerState.PAUSED:
772
- self._state = CrawlerState.RUNNING
773
- logger.info("Resume command received")
774
-
775
- elif msg.command == CrawlCommand.REPRIORITIZE:
776
- url = msg.data.get("url")
777
- priority = msg.data.get("priority")
778
- if url and priority is not None:
779
- self._scheduler.reprioritize_url(url, priority)
780
- logger.debug("Reprioritized %s to %s", url, priority)
781
-
782
- elif msg.command == CrawlCommand.FORCE_VISIT:
783
- url = msg.data.get("url")
784
- if url:
785
- # Додаємо URL в чергу з високим пріоритетом
786
- node_class = self.config.custom_node_class or Node
787
- node = node_class(url=url, depth=0, plugin_manager=self.node_plugin_manager)
788
- self._graph.add_node(node)
789
- self._scheduler.add_node(node, priority=100)
790
- logger.debug("Force visit scheduled: %s", url)
791
-
769
+ def _execute_command(self, msg: ControlMessage) -> str | None:
770
+ if msg.command == CrawlCommand.STOP:
771
+ reason = msg.data.get("reason", "Stop requested via control channel")
772
+ logger.info("Stop command received: %s", reason)
773
+ return reason
774
+ if msg.command == CrawlCommand.PAUSE:
775
+ self._state = CrawlerState.PAUSED
776
+ logger.info("Pause command received")
777
+ elif msg.command == CrawlCommand.RESUME:
778
+ if self._state == CrawlerState.PAUSED:
779
+ self._state = CrawlerState.RUNNING
780
+ logger.info("Resume command received")
781
+ elif msg.command == CrawlCommand.REPRIORITIZE:
782
+ self._reprioritize(msg)
783
+ elif msg.command == CrawlCommand.FORCE_VISIT:
784
+ self._force_visit(msg)
792
785
  return None
793
786
 
787
+ def _reprioritize(self, msg: ControlMessage) -> None:
788
+ url = msg.data.get("url")
789
+ priority = msg.data.get("priority")
790
+ if not url or priority is None:
791
+ self._warn_invalid_command(msg, "requires data['url'] and data['priority']")
792
+ return
793
+ self._scheduler.reprioritize_url(url, priority)
794
+ logger.debug("Reprioritized %s to %s", url, priority)
795
+
796
+ def _force_visit(self, msg: ControlMessage) -> None:
797
+ url = msg.data.get("url")
798
+ if not url:
799
+ self._warn_invalid_command(msg, "requires data['url']")
800
+ return
801
+ node_class = self.config.custom_node_class or Node
802
+ node = node_class(url=url, depth=0, plugin_manager=self.node_plugin_manager)
803
+ self._graph.add_node(node)
804
+ self._scheduler.add_node(node, priority=100)
805
+ logger.debug("Force visit scheduled: %s", url)
806
+
807
+ def _warn_unsupported_command(self, msg: ControlMessage) -> None:
808
+ if msg.command in self._warned_unsupported_commands:
809
+ logger.debug("Unsupported control command ignored: %s from %s", msg.command.value, msg.sender)
810
+ return
811
+ self._warned_unsupported_commands.add(msg.command)
812
+ supported = ", ".join(sorted(command.value for command in SPIDER_COMMANDS))
813
+ logger.warning(
814
+ "Unsupported control command %r from %s is ignored (supported: %s)",
815
+ msg.command.value,
816
+ msg.sender,
817
+ supported,
818
+ )
819
+
820
+ @staticmethod
821
+ def _warn_invalid_command(msg: ControlMessage, problem: str) -> None:
822
+ logger.warning(
823
+ "Control command %r from %s is ignored: %s, got %r",
824
+ msg.command.value,
825
+ msg.sender,
826
+ problem,
827
+ msg.data,
828
+ )
829
+
794
830
  async def check_control_channel(self) -> str | None:
795
831
  """Асинхронна обгортка над ``process_control_messages``."""
796
832
  return self.process_control_messages()