graph-crawler 5.2.1__tar.gz → 5.3.3__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 (494) hide show
  1. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/CHANGELOG.md +179 -27
  2. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/MANIFEST.in +0 -3
  3. graph_crawler-5.3.3/PKG-INFO +531 -0
  4. graph_crawler-5.3.3/README.md +410 -0
  5. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/__init__.py +34 -16
  6. graph_crawler-5.3.3/graph_crawler/__version__.py +8 -0
  7. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/ai/__init__.py +5 -4
  8. graph_crawler-5.3.3/graph_crawler/api/__init__.py +28 -0
  9. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/_core.py +38 -34
  10. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/_distributed.py +14 -16
  11. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/_shared.py +11 -21
  12. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/_sitemap_distributed.py +20 -26
  13. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/async_.py +40 -40
  14. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/cli.py +1 -1
  15. graph_crawler-5.3.3/graph_crawler/api/client/__init__.py +8 -0
  16. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/client/client.py +51 -76
  17. graph_crawler-5.3.3/graph_crawler/api/history_manager.py +55 -0
  18. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/project_init.py +2 -0
  19. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/stats_collector.py +12 -39
  20. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/sync.py +105 -56
  21. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/bootstrap.py +42 -40
  22. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/mappers/edge_mapper.py +1 -1
  23. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/mappers/graph_mapper.py +15 -7
  24. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/mappers/node_mapper.py +34 -4
  25. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/driver_factory.py +2 -2
  26. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/node_exporter.py +2 -1
  27. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/finalize.py +4 -4
  28. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/low_memory_graph_saver.py +3 -5
  29. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/scheduling/rule_based_scheduler.py +12 -20
  30. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/adaptive_throttler.py +2 -3
  31. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/async_bridge.py +1 -1
  32. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/base_spider.py +4 -5
  33. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/celery_batch_spider.py +9 -11
  34. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/celery_spider.py +8 -11
  35. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/checkpoint.py +5 -5
  36. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/crawl_coordinator.py +23 -18
  37. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/filters/domain_filter.py +1 -1
  38. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/filters/domain_patterns.py +2 -2
  39. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/link_processor.py +36 -59
  40. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/multiprocess_spider.py +4 -4
  41. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/node_scanner.py +98 -39
  42. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/scheduler.py +26 -16
  43. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/sitemap_parser.py +6 -11
  44. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/sitemap_spider.py +6 -6
  45. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/spider.py +16 -8
  46. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/spider_lifecycle.py +2 -2
  47. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/worker_pool.py +1 -1
  48. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/graph_merge/graph_operations.py +3 -3
  49. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/data/backends/memory.py +1 -1
  50. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/data/backends/sqlite.py +2 -2
  51. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/data/interfaces.py +6 -18
  52. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/context/crawl_context.py +6 -6
  53. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/graph.py +109 -60
  54. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/graph_operations.py +2 -5
  55. graph_crawler-5.3.3/graph_crawler/domain/entities/graph_statistics.py +10 -0
  56. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/merge_strategies.py +2 -5
  57. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/node.py +148 -30
  58. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/registries.py +6 -20
  59. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/__init__.py +1 -2
  60. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/base.py +4 -2
  61. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/crawl.py +13 -24
  62. graph_crawler-5.3.3/graph_crawler/domain/events/edge.py +43 -0
  63. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/event_bus.py +5 -6
  64. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/events.py +10 -10
  65. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/fetch.py +13 -16
  66. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/legacy_type_map.py +3 -3
  67. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/llm.py +10 -20
  68. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/node.py +12 -25
  69. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/plugin.py +7 -11
  70. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/scheduler.py +8 -12
  71. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/storage.py +10 -17
  72. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/__init__.py +1 -1
  73. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/distributed_spider.py +4 -5
  74. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/driver.py +1 -2
  75. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/event_bus.py +4 -4
  76. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/exporters.py +9 -23
  77. graph_crawler-5.3.3/graph_crawler/domain/interfaces/merge_context.py +52 -0
  78. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/node_interfaces.py +39 -26
  79. graph_crawler-5.3.3/graph_crawler/domain/interfaces/parser.py +88 -0
  80. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/storage/base.py +0 -2
  81. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/storage/eviction.py +8 -12
  82. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/eviction_policy.py +1 -1
  83. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/minimap_builder.py +4 -4
  84. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/node_view_builder.py +7 -7
  85. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/pagination_guards.py +1 -1
  86. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/url_normalizer.py +12 -3
  87. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/url_rule_matcher.py +16 -8
  88. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/__init__.py +1 -1
  89. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/configs.py +16 -9
  90. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/html_document.py +1 -1
  91. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/lifecycle.py +1 -1
  92. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/models.py +11 -12
  93. graph_crawler-5.3.3/graph_crawler/domain/value_objects/page_actions.py +72 -0
  94. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/url_normalization.py +3 -2
  95. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/base.py +14 -25
  96. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/chain.py +6 -10
  97. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/proxy_health.py +6 -9
  98. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/proxy_middleware.py +6 -5
  99. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/proxy_models.py +11 -15
  100. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/proxy_selection.py +5 -5
  101. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/retry_middleware.py +2 -2
  102. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/user_agent_middleware.py +1 -1
  103. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/builtin/stats_export_plugin.py +9 -8
  104. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/crawl_engine/smart_crawl.py +11 -21
  105. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/__init__.py +1 -1
  106. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/anti_bot_detection.py +4 -1
  107. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/anti_bot_playwright.py +4 -1
  108. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/anti_bot_scripts.py +7 -4
  109. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/anti_bot_stealth.py +4 -4
  110. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/__init__.py +6 -6
  111. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/base.py +100 -12
  112. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/links.py +1 -1
  113. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/metadata.py +7 -14
  114. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/smart_page_finder.py +1 -1
  115. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/adapters/selectolax_adapter.py +1 -2
  116. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/celery_app.py +1 -1
  117. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/celery_job_task.py +1 -1
  118. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/celery_unified.py +1 -1
  119. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/easy_crawler.py +1 -1
  120. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/worker_api.py +2 -2
  121. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/auto_storage.py +1 -1
  122. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/graph_repository.py +1 -1
  123. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/lmdb_eviction_storage.py +1 -3
  124. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/memory_dedup_store.py +2 -3
  125. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/memory_politeness_store.py +1 -1
  126. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/driver.py +5 -5
  127. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/driver_curl_cffi.py +1 -1
  128. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/autothrottle_adaptive.py +7 -7
  129. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/http_cache.py +2 -2
  130. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/retry.py +2 -2
  131. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/base.py +36 -11
  132. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/base_plugin.py +1 -1
  133. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/core/body_decoder.py +1 -1
  134. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/core/mixins.py +2 -2
  135. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/escalation.py +81 -22
  136. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/batch_scheduler.py +2 -2
  137. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/browser_factory.py +5 -1
  138. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/browser_profile.py +1 -1
  139. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/engine.py +15 -0
  140. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/fetch_metrics.py +1 -1
  141. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/net/__init__.py +1 -1
  142. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/net/proxy_pool.py +1 -1
  143. graph_crawler-5.3.3/graph_crawler/infrastructure/transport/playwright/page_actions.py +186 -0
  144. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/page_fetch.py +74 -2
  145. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/captcha_solver.py +1 -1
  146. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/cookie_consent.py +6 -6
  147. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/human_bezier.py +1 -1
  148. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/network_log.py +41 -1
  149. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/pool_lifecycle.py +1 -1
  150. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/pooled_driver.py +14 -6
  151. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/plugin_manager.py +4 -4
  152. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/protocols.py +19 -5
  153. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/native/__init__.py +7 -8
  154. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/native/setup.py +12 -10
  155. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/error_tracing.py +25 -27
  156. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/metrics/prometheus_metrics.py +1 -1
  157. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/metrics_core.py +21 -27
  158. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/structured_logging.py +31 -55
  159. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/optimizations/simhash_numba.py +1 -1
  160. graph_crawler-5.3.3/graph_crawler/py.typed +0 -0
  161. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/constants.py +7 -2
  162. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/platform/adaptive_resolver.py +3 -3
  163. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/platform/config.py +2 -2
  164. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/platform/detector.py +1 -1
  165. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/bloom_filter.py +1 -1
  166. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/event_publisher_mixin.py +1 -2
  167. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/fast_json.py +1 -1
  168. graph_crawler-5.3.3/graph_crawler/shared/utils/isolated_executor.py +193 -0
  169. graph_crawler-5.3.3/graph_crawler.egg-info/PKG-INFO +531 -0
  170. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler.egg-info/SOURCES.txt +4 -2
  171. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/pyproject.toml +2 -2
  172. graph_crawler-5.2.1/CODE_STYLE.md +0 -1473
  173. graph_crawler-5.2.1/PKG-INFO +0 -370
  174. graph_crawler-5.2.1/README.md +0 -249
  175. graph_crawler-5.2.1/graph_crawler/__version__.py +0 -7
  176. graph_crawler-5.2.1/graph_crawler/api/__init__.py +0 -24
  177. graph_crawler-5.2.1/graph_crawler/api/client/__init__.py +0 -8
  178. graph_crawler-5.2.1/graph_crawler/api/history_manager.py +0 -82
  179. graph_crawler-5.2.1/graph_crawler/domain/entities/graph_statistics.py +0 -13
  180. graph_crawler-5.2.1/graph_crawler/domain/events/edge.py +0 -46
  181. graph_crawler-5.2.1/graph_crawler/domain/interfaces/merge_context.py +0 -58
  182. graph_crawler-5.2.1/graph_crawler/domain/interfaces/parser.py +0 -110
  183. graph_crawler-5.2.1/graph_crawler/native/README.md +0 -110
  184. graph_crawler-5.2.1/graph_crawler.egg-info/PKG-INFO +0 -370
  185. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/LICENSE +0 -0
  186. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/server/__init__.py +0 -0
  187. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/server/crawl_monitor.py +0 -0
  188. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/server/dashboard.py +0 -0
  189. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/server/rest_api.py +0 -0
  190. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/server/webhooks.py +0 -0
  191. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/api/server/websocket_manager.py +0 -0
  192. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/__init__.py +0 -0
  193. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/context/__init__.py +0 -0
  194. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/context/dependency_registry.py +0 -0
  195. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/context/graph_context.py +0 -0
  196. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/context/merge_context.py +0 -0
  197. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/__init__.py +0 -0
  198. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/edge_dto.py +0 -0
  199. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/graph_dto.py +0 -0
  200. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/mappers/__init__.py +0 -0
  201. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/mappers/node_data_codec.py +0 -0
  202. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/node_dto.py +0 -0
  203. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/dto/utils.py +0 -0
  204. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/__init__.py +0 -0
  205. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/application_container.py +0 -0
  206. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/__init__.py +0 -0
  207. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/base_exporter.py +0 -0
  208. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/csv_exporter.py +0 -0
  209. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/edge_exporter.py +0 -0
  210. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/excel_exporter.py +0 -0
  211. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/jsonl_exporter.py +0 -0
  212. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/parquet_exporter.py +0 -0
  213. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/exporters/sql_exporter.py +0 -0
  214. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/resource_budget.py +0 -0
  215. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/scheduling/__init__.py +0 -0
  216. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/services/storage_factory.py +0 -0
  217. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/__init__.py +0 -0
  218. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/__init__.py +0 -0
  219. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/dead_letter_queue.py +0 -0
  220. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/domain_rate_limiter.py +0 -0
  221. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/filters/__init__.py +0 -0
  222. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/filters/base.py +0 -0
  223. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/filters/path_filter.py +0 -0
  224. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/incremental_strategy.py +0 -0
  225. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/parsers/__init__.py +0 -0
  226. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/parsers/base.py +0 -0
  227. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/parsers/html_parser.py +0 -0
  228. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/progress_tracker.py +0 -0
  229. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/serialization_mixin.py +0 -0
  230. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/crawling/sitemap_processor.py +0 -0
  231. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/graph_export.py +0 -0
  232. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/graph_merge/__init__.py +0 -0
  233. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/application/use_cases/graph_merge/merge_strategies.py +0 -0
  234. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/data/__init__.py +0 -0
  235. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/data/backends/__init__.py +0 -0
  236. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/__init__.py +0 -0
  237. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/context/__init__.py +0 -0
  238. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/__init__.py +0 -0
  239. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/edge.py +0 -0
  240. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/edge_analysis.py +0 -0
  241. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/redirect_mode.py +0 -0
  242. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/sitemap_node.py +0 -0
  243. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/entities/strategies.py +0 -0
  244. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/events/idempotent_subscriber.py +0 -0
  245. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/adapter.py +0 -0
  246. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/control_channel.py +0 -0
  247. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/crawl_context.py +0 -0
  248. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/filter.py +0 -0
  249. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/plugin_manager.py +0 -0
  250. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/processor.py +0 -0
  251. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/scanner.py +0 -0
  252. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/scheduler.py +0 -0
  253. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/scheduling/__init__.py +0 -0
  254. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/scheduling/frontier.py +0 -0
  255. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/scheduling/scheduler.py +0 -0
  256. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/spider.py +0 -0
  257. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/stop_condition.py +0 -0
  258. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/storage/__init__.py +0 -0
  259. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/interfaces/storage/queue.py +0 -0
  260. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/__init__.py +0 -0
  261. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/adjacency_maintenance.py +0 -0
  262. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/cycle_detection.py +0 -0
  263. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/degree_ranking.py +0 -0
  264. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/depth_context.py +0 -0
  265. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/display_title.py +0 -0
  266. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/duplicate_detection.py +0 -0
  267. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/duplicate_selector.py +0 -0
  268. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/duplicate_stats.py +0 -0
  269. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/edge_filter.py +0 -0
  270. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/edge_statistics.py +0 -0
  271. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/graph_query_cascade.py +0 -0
  272. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/graph_statistics.py +0 -0
  273. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/graph_traversal.py +0 -0
  274. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/language_versions.py +0 -0
  275. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/lsh_index.py +0 -0
  276. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/node_content_resolver.py +0 -0
  277. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/predicate_ops.py +0 -0
  278. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/schema_filter.py +0 -0
  279. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/search_haystack.py +0 -0
  280. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/simhash_clustering.py +0 -0
  281. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/simhash_fallback.py +0 -0
  282. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/similarity_ranking.py +0 -0
  283. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/text_search.py +0 -0
  284. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/url_clustering.py +0 -0
  285. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/url_deduplication.py +0 -0
  286. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/services/url_tree_builder.py +0 -0
  287. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/domain_patterns.py +0 -0
  288. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/fetch_options.py +0 -0
  289. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/redirect_hop.py +0 -0
  290. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/scheduling/__init__.py +0 -0
  291. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/scheduling/crawl_request.py +0 -0
  292. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/scheduling/retry_policy.py +0 -0
  293. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/settings.py +0 -0
  294. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/domain/value_objects/views.py +0 -0
  295. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/__init__.py +0 -0
  296. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/__init__.py +0 -0
  297. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/cache_middleware.py +0 -0
  298. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/error_recovery_middleware.py +0 -0
  299. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/logging_middleware.py +0 -0
  300. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/rate_limit_middleware.py +0 -0
  301. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/request_response_middleware.py +0 -0
  302. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/robots_cache.py +0 -0
  303. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/robots_middleware.py +0 -0
  304. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/middleware/robots_validator.py +0 -0
  305. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/__init__.py +0 -0
  306. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/builtin/__init__.py +0 -0
  307. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/crawl_engine/__init__.py +0 -0
  308. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/crawl_engine/base.py +0 -0
  309. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/crawl_engine/priority_provider.py +0 -0
  310. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/base.py +0 -0
  311. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/captcha/__init__.py +0 -0
  312. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/captcha/detector.py +0 -0
  313. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/captcha/models.py +0 -0
  314. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/captcha/plugin.py +0 -0
  315. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/engine/captcha/services.py +0 -0
  316. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/content_extractors/__init__.py +0 -0
  317. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/content_extractors/base.py +0 -0
  318. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/content_extractors/goose3_extractor.py +0 -0
  319. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/content_extractors/newspaper_extractor.py +0 -0
  320. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/content_extractors/plugin.py +0 -0
  321. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/content_extractors/readability_extractor.py +0 -0
  322. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/defaults.py +0 -0
  323. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/extractors/__init__.py +0 -0
  324. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/extractors/email_extractor.py +0 -0
  325. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/extractors/phone_extractor.py +0 -0
  326. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/extractors/price_extractor.py +0 -0
  327. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/markdown_plugin.py +0 -0
  328. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/__init__.py +0 -0
  329. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/constants.py +0 -0
  330. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/exceptions.py +0 -0
  331. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/extractor.py +0 -0
  332. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/options.py +0 -0
  333. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/__init__.py +0 -0
  334. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/base.py +0 -0
  335. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/jsonld.py +0 -0
  336. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/microdata.py +0 -0
  337. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/opengraph.py +0 -0
  338. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/rdfa.py +0 -0
  339. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/parsers/twitter.py +0 -0
  340. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/plugin.py +0 -0
  341. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/structured_data/result.py +0 -0
  342. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/text.py +0 -0
  343. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/node/url_normalizer.py +0 -0
  344. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/extensions/plugins/registry.py +0 -0
  345. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/__init__.py +0 -0
  346. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/adapters/__init__.py +0 -0
  347. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/adapters/base.py +0 -0
  348. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/__init__.py +0 -0
  349. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/celery_batch.py +0 -0
  350. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/messaging/config.py +0 -0
  351. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/__init__.py +0 -0
  352. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/base.py +0 -0
  353. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/eviction.py +0 -0
  354. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/json/__init__.py +0 -0
  355. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/json_storage.py +0 -0
  356. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/memory/__init__.py +0 -0
  357. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/memory_frontier.py +0 -0
  358. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/memory_storage.py +0 -0
  359. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/mongodb_storage.py +0 -0
  360. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/naming_strategy.py +0 -0
  361. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/postgresql_storage.py +0 -0
  362. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/protocols/__init__.py +0 -0
  363. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/redis_dedup_store.py +0 -0
  364. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/redis_politeness_store.py +0 -0
  365. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/repository.py +0 -0
  366. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/sqlite/__init__.py +0 -0
  367. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/sqlite_eviction_storage.py +0 -0
  368. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/sqlite_storage.py +0 -0
  369. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/__init__.py +0 -0
  370. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/file_job_storage.py +0 -0
  371. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/file_queue_storage.py +0 -0
  372. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/memory_job_storage.py +0 -0
  373. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/memory_queue_storage.py +0 -0
  374. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/postgresql_job_storage.py +0 -0
  375. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/postgresql_queue_storage.py +0 -0
  376. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/persistence/unified/unified_storage.py +0 -0
  377. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/__init__.py +0 -0
  378. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/__init__.py +0 -0
  379. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/config.py +0 -0
  380. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/context.py +0 -0
  381. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/__init__.py +0 -0
  382. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/autothrottle.py +0 -0
  383. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/headers.py +0 -0
  384. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/plugins/rate_limiter.py +0 -0
  385. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/async_http/stages.py +0 -0
  386. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/compression/__init__.py +0 -0
  387. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/compression/strategy.py +0 -0
  388. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/connection_pool.py +0 -0
  389. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/context.py +0 -0
  390. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/core/__init__.py +0 -0
  391. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/core/base_async.py +0 -0
  392. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/core/redirect_utils.py +0 -0
  393. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/factory.py +0 -0
  394. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/__init__.py +0 -0
  395. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/adaptive_semaphore.py +0 -0
  396. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/config.py +0 -0
  397. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/context.py +0 -0
  398. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/driver.py +0 -0
  399. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/net/geoip.py +0 -0
  400. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/net/rate_limiter.py +0 -0
  401. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/__init__.py +0 -0
  402. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/_network_record.py +0 -0
  403. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/captcha_detector.py +0 -0
  404. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/form_filler.py +0 -0
  405. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/ram_adaptive.py +0 -0
  406. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/screenshot.py +0 -0
  407. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/plugins/user_profile.py +0 -0
  408. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/stages.py +0 -0
  409. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/playwright/stealth_hints.py +0 -0
  410. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/undetected/__init__.py +0 -0
  411. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/infrastructure/transport/undetected/async_driver.py +0 -0
  412. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/native/bloom_filter.pyx +0 -0
  413. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/native/html_parser.pyx +0 -0
  414. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/native/url_utils.pyx +0 -0
  415. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/__init__.py +0 -0
  416. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/decorators/__init__.py +0 -0
  417. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/decorators/cache.py +0 -0
  418. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/decorators/log.py +0 -0
  419. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/decorators/retry.py +0 -0
  420. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/decorators/timing.py +0 -0
  421. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/extensions/__init__.py +0 -0
  422. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/extensions/memory_guard.py +0 -0
  423. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/extensions/state_manager.py +0 -0
  424. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/__init__.py +0 -0
  425. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/base.py +0 -0
  426. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/base_metrics_listener.py +0 -0
  427. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/crawl_listener.py +0 -0
  428. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/dlq_listener.py +0 -0
  429. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/error_listener.py +0 -0
  430. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/logging_listener.py +0 -0
  431. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/metrics_listener.py +0 -0
  432. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/node_listener.py +0 -0
  433. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/plugin_listener.py +0 -0
  434. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/storage_listener.py +0 -0
  435. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/listeners/url_listener.py +0 -0
  436. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/metrics/__init__.py +0 -0
  437. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/metrics/memory_profiler.py +0 -0
  438. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/metrics/metrics_collector.py +0 -0
  439. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/observability/tracing.py +0 -0
  440. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/optimizations/__init__.py +0 -0
  441. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/optimizations/generators.py +0 -0
  442. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/optimizations/memory_slots.py +0 -0
  443. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/__init__.py +0 -0
  444. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/dto/__init__.py +0 -0
  445. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/dto/edge_dto.py +0 -0
  446. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/dto/graph_dto.py +0 -0
  447. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/dto/node_dto.py +0 -0
  448. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/error_handling/__init__.py +0 -0
  449. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/error_handling/error_handler.py +0 -0
  450. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/exceptions.py +0 -0
  451. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/platform/__init__.py +0 -0
  452. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/platform/diagnostics.py +0 -0
  453. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/platform/metrics.py +0 -0
  454. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/security/__init__.py +0 -0
  455. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/security/url_sanitizer.py +0 -0
  456. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/security/url_validator.py +0 -0
  457. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/__init__.py +0 -0
  458. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/async_compat.py +0 -0
  459. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/captcha/__init__.py +0 -0
  460. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/captcha/base.py +0 -0
  461. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/captcha/manager.py +0 -0
  462. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/celery_config.py +0 -0
  463. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/celery_helpers.py +0 -0
  464. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/cgroup_limits.py +0 -0
  465. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/content_decoder.py +0 -0
  466. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/distributed_rate_limiter.py +0 -0
  467. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/distributed_rate_limiter_backends.py +0 -0
  468. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/dns_cache.py +0 -0
  469. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/enterprise_ua_generator.py +0 -0
  470. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/fingerprint.py +0 -0
  471. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/fingerprint_data.py +0 -0
  472. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/fingerprint_generators.py +0 -0
  473. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/fingerprint_profile.py +0 -0
  474. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/fingerprint_stealth.py +0 -0
  475. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/html_utils.py +0 -0
  476. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/id_generator.py +0 -0
  477. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/memory_optimizer.py +0 -0
  478. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/rate_limiter.py +0 -0
  479. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/regex_validator.py +0 -0
  480. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/shadow_dom.py +0 -0
  481. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/text_utils.py +0 -0
  482. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/url_patterns.py +0 -0
  483. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/url_utils.py +0 -0
  484. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/user_agent_rotator.py +0 -0
  485. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/validation_helpers.py +0 -0
  486. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/visualization.py +0 -0
  487. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/shared/utils/visualization_core.py +0 -0
  488. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/tools/__init__.py +0 -0
  489. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler/tools/db_cleanup.py +0 -0
  490. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler.egg-info/dependency_links.txt +0 -0
  491. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler.egg-info/entry_points.txt +0 -0
  492. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler.egg-info/requires.txt +0 -0
  493. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/graph_crawler.egg-info/top_level.txt +0 -0
  494. {graph_crawler-5.2.1 → graph_crawler-5.3.3}/setup.cfg +0 -0
@@ -1,8 +1,155 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.3.3 — 2026-10
4
+
5
+ ### Added
6
+
7
+ - **`IsolatedExecutor` / `IsolatedTaskError`** (`from graph_crawler import IsolatedExecutor`). Runs CPU-bound functions
8
+ in a `spawn` process pool: a native crash (SIGSEGV in libxml2/lxml, trafilatura, any C extension) or a hang of a
9
+ worker becomes `IsolatedTaskError(reason="crashed" | "timeout" | "unavailable")` in the caller, the pool restarts
10
+ and the crawl goes on. Tasks that shared the broken pool are retried once; a hung worker is killed after
11
+ `timeout` (default `DEFAULT_ISOLATED_TASK_TIMEOUT` = 30 s). Ordinary exceptions of the function propagate as is.
12
+ - **`CrawlerConfig.node_enrich_timeout`** and `crawl(..., node_enrich_timeout=...)` / `async_crawl(...)` /
13
+ `GraphCrawlerClient.crawl(...)`: per-node enrichment limit (HTML parse, plugins, subclass hook, hashes).
14
+ `None` keeps `DEFAULT_NODE_ENRICH_TIMEOUT` (45 s, `shared.constants`). `NodeScanner(driver, enrich_timeout=...)`.
15
+ - `run_in_html_pool(fn, *args)` (`graph_crawler.domain.entities.node`): runs `fn` in the HTML executor under its
16
+ semaphore. `Node.pop_discovered_links()` returns links extracted on `ON_HTML_PARSED` of an interrupted
17
+ `enrich_node`.
18
+ - `URLRuleMatcher.rule_matches(rule, url)`.
19
+
20
+ ### Changed
21
+
22
+ - **The `Node._update_from_context` hook and `get_content_hash`/`get_simhash` run in the HTML executor** under the
23
+ same semaphore as HTML parsing (they used the default `asyncio.to_thread` pool, `min(32, cpu+4)` threads, outside
24
+ the CPU budget). Concurrency of heavy subclass code is now bounded by `GRAPH_CRAWLER_HTML_PARSER_WORKERS`
25
+ (default — the cgroup CPU quota); `=1` serialises it completely. Code that is not thread-safe must use its own
26
+ lock or `GRAPH_CRAWLER_HTML_PARSER_WORKERS=1`; native code that can crash — `IsolatedExecutor`.
27
+ - **One URL rule — one decision.** `CrawlScheduler`, `LinkProcessor` and `RuleBasedScheduler` share
28
+ `URLRuleMatcher`: first match in list order, `SmartURLRule` is matched against its `scope` (the scheduler used to
29
+ run `re.search` over the full URL, so `SmartURLRule(r"/help", scope=PATH, should_scan=False)` excluded
30
+ `https://helpware.com/`). `GraphSpider` no longer copies `url_rules` with `should_scan=False` into `PathFilter`
31
+ (it applied them to the path, ignoring order and scope); `excluded_paths` / `included_paths` are unchanged.
32
+ - A URL rule whose `priority` is outside `[MIN_URL_PRIORITY, MAX_URL_PRIORITY]` (`[-100, 100]`) gets a WARNING when
33
+ the scheduler is created: `priority` is a queue position and is clamped; rule precedence is list order.
34
+ - `IDriver.fetch(url, *, referer, extra_headers, cookies)` is the contract of every driver: `BaseDriver`,
35
+ `EscalatingDriver` (caller cookies/headers merged with the host session — the session wins on a cookie name
36
+ clash, the session `User-Agent` is applied on top for HTTP/TLS tiers), `PooledPlaywrightDriver` (`extra_headers`),
37
+ `IAsyncDriver`, `IBrowserDriver`. `BaseDriver.fetch_many` passes per-URL `extra_headers_list` / `cookies_list` to
38
+ `fetch` (they were dropped).
39
+
40
+ ### Fixed
41
+
42
+ - **Per-node timeout no longer drops discovered links.** `scan_node` / `scan_batch` return the links extracted on
43
+ `ON_HTML_PARSED` when enrichment exceeds the limit; the node is still `ERROR` / `FAILED`. Large listing pages, the
44
+ first to hit the limit, keep their edges.
45
+
46
+ ## 5.3.2 — 2026-06
47
+
48
+ ### Added
49
+
50
+ - **Fetch provenance on the node.** `Node.channel`, `bot_block_status`, `served_by_driver`, `served_by_engine`,
51
+ `escalations` and `fetch_info` read `metadata["fetch"]`; `Node.ok` (scanned, 2xx/3xx, not blocked, no error) and
52
+ `Node.is_blocked` separate useful pages from block and error pages.
53
+ - **Plugin routing.** `BaseNodePlugin.domains` (domain and subdomains) and `url_pattern` (regex), on the class or via
54
+ `config={"domains": [...], "url_pattern": ...}`, plus an overridable `applies_to(url)`. An invalid `url_pattern`
55
+ raises `ConfigurationError` on `register()`. Several instances of one plugin class with different routes run
56
+ side by side (they used to replace each other).
57
+ - **Typed plugin output.** `NodePluginContext.set_node_field(name, value)` writes a field declared by a `Node`
58
+ subclass, validated against its annotation (`"100"` → `int`); base fields are read-only. Fields are rolled back
59
+ together with `metadata`/`user_data` when the plugin fails.
60
+ - **Empty extraction warning.** `BaseNodePlugin.expects_nonempty` + `extraction_key`: a WARNING when the plugin left
61
+ `user_data[extraction_key]` empty.
62
+ - **`Graph.search(..., user_data_keys=("jobs",))`** searches the listed `user_data` keys as well.
63
+ - **`Node.public_user_data`** and `RESERVED_USER_DATA_KEYS` (`_link_anchors`, `_custom_fields`, `_node_class`).
64
+
65
+ ### Changed
66
+
67
+ - A response with status 400 or higher gets `metadata["error"] = "HTTP <code>"` and is logged as a WARNING
68
+ (`HTTP 404 for <url> — page not scanned`).
69
+ - A bare 403/401/405 without vendor markers is still classified as `none`; the decision is now logged at INFO.
70
+ - Quieter logs: import writes nothing at INFO, platform detection runs once, the start of a crawl is one line,
71
+ and each page is one INFO line with `[channel=…]`, `[block=…]`, `[error=…]`.
72
+ - The temporary `_link_anchors` map is removed from the node once its edges are created (the anchor text stays in
73
+ `edge.metadata["anchor_text"]`). The default JSON export writes `public_user_data`.
74
+ - A third-party plugin that overwrites a reserved `user_data` key gets a WARNING.
75
+
76
+ ### Fixed
77
+
78
+ - `load_graph()` without `node_class=` restores a custom `Node` subclass when its module is already imported (the
79
+ class name is stored with the node; nothing is imported from the file). `Graph.copy()`, `union`, `intersection`,
80
+ `difference` and nodes created in a loaded graph keep the node class.
81
+ - `metadata` written by a plugin on `ON_NODE_CREATED` or `ON_AFTER_SCAN` is no longer lost.
82
+ - **Placeholder lists are detected as a JavaScript shell.** A 2xx body with five or more `placeholder` /
83
+ `skeleton` / `shimmer` elements makes `needs_browser` true, so `escalating` goes straight to the browser tier.
84
+ With `driver="http"` the node gets `metadata["js_shell_suspected"]` and a WARNING (once per host) suggests
85
+ `driver="escalating"` or `"playwright"`.
86
+ - **No duplicate edges.** A link repeated on a page (menu + footer + body) created a new `Edge` each time
87
+ (17.5 % of edges on uk.wikipedia); `LinkProcessor` now skips existing `(source, target)` pairs. `Graph.has_edge`
88
+ also sees the backend edge index.
89
+ - **`h1` children are no longer joined with spaces** (`Портал : Поточні події` → `Портал:Поточні події`).
90
+ - **Default ports are normalized** (`http://x:80`, `https://x:443`, empty port).
91
+ - `Graph.normalize_url()` is public; `LinkProcessor` checks the scheduler with the canonical form as well.
92
+ - **`NetworkLogPlugin` waits for network quiet** before publishing the log (`settle_ms`=2000, `quiet_ms`=300;
93
+ `settle_ms=0` restores the old behaviour) — late XHR (search/listing after hydration) was lost.
94
+ - `create_trailing_slash_rule` keeps the root path; an empty path is `/` for PATH-scope rules.
95
+
96
+ ## 5.3.1 — 2026-06
97
+
98
+ ### Added
99
+
100
+ - **Hydration state globals snapshot.** The Playwright driver serialises `window.__NAME__` objects (`__NUXT__`,
101
+ `__APOLLO_STATE__`, `__INITIAL_STATE__`, `__PINIA__`, …) with `JSON.stringify` after the page has run and embeds
102
+ them into the HTML snapshot as `<script type="application/json" data-agc-state="__NAME__">`. Nuxt 2 ships its
103
+ state as an IIFE (`window.__NUXT__=(function(a,b){return {…}}(…))`) which is JavaScript, not JSON, and was
104
+ invisible to any static state parser. Globals that already exist as a JSON `<script id="__NAME__">`
105
+ (`__NEXT_DATA__`) are not duplicated; `PageFetcher.state_globals_max_chars` (5 MB) caps the snapshot.
106
+ `inject_state_globals(html, states)` is exported for tests and other drivers.
107
+
108
+ ## 5.3.0 — 2026-06
109
+
110
+ ### Added
111
+
112
+ - **Page actions as public API.** `driver_config={"actions": [PageAction(...) | dict, ...]}` for the Playwright driver:
113
+ `click` / `fill` / `select` / `press` / `wait_for(response=…|selector=…)` / `scroll`, target by `selector`, `text`,
114
+ `placeholder` or `label` (`nth` among visible matches). Actions are validated when the driver is created and run
115
+ after content is ready and before the HTML snapshot and `CONTENT_READY` hooks — the network log is published after
116
+ the actions. Results land in `node.metadata["actions_log"]`, `["requests_after_actions"]` and `["actions_error"]`
117
+ (the first failing action stops the chain with a reason). `from graph_crawler import PageAction`.
118
+ - `engine.page_error_types()` — page error classes of all installed browser SDKs.
119
+ - `PageAction(type="back")`; action errors name the element that intercepts pointer events.
120
+
121
+ ### Fixed
122
+
123
+ - Binary request bodies (gzip beacons) no longer raise `UnicodeDecodeError` inside Playwright event listeners
124
+ (`NetworkLogPlugin` and the action recorder read `post_data_buffer` with `errors="replace"`); the error used to break
125
+ the next Playwright call with `TypeError: function takes exactly 5 arguments`.
126
+
127
+ ### Fixed
128
+
129
+ - `graph.get_node_by_url(seed)` finds the node after a redirect in the graph returned by `async_crawl`: `add_node`
130
+ restores the alias from `metadata["was_redirected_from"]`.
131
+
132
+ ## 5.2.2 — 2026-06
133
+
134
+ ### Fixed (test infrastructure)
135
+
136
+ - **The library no longer configures logging.** `GraphCrawlerClient.__init__` called `logging.basicConfig(INFO)`
137
+ when its logger had no handlers, so every crawl printed to stderr and reconfigured the application's root
138
+ logger. Removed; `graph_crawler` gets a `NullHandler`.
139
+ - **Noise demoted:** `HTML executor shutdown` and `lmdb not installed … Falling back to SQLiteEvictionStorage` are
140
+ DEBUG; `channel='chrome' not installed, falling back to bundled Chromium` is a WARNING once per process.
141
+ - **One version source.** `graph_crawler.__version__` (and the default User-Agent) come from the installed
142
+ package metadata (`pyproject.toml`); `__version__.py` no longer hard-codes a number that drifted (5.1.0 vs 5.2.0).
143
+ - **Test suite collects without optional extras.** Tests that need `psutil`, `rapidfuzz`, `stop-words`,
144
+ `html2md-clean` or `playwright` are skipped via `importorskip` instead of failing collection under `-x`.
145
+ `test_div_inline_fix.py` / `test_phase1_fixes.py` tested `html2md_clean` itself (no `graph_crawler` code) and were
146
+ removed from the core suite.
147
+ - GitLab CI (`.gitlab-ci.yml`): offline suite with `[dev]` and a minimal job without extras.
148
+
3
149
  ## 5.2.1 — 2026-06
4
150
 
5
- ### Fixed (freelance cases 33/34 — `ai-graph-crawler/docs/FREELANCE_CASES.md`)
151
+ ### Fixed (freelance cases)
152
+
6
153
  - **Captured XHR/fetch traffic could not be replayed outside the browser.** `NetworkLogPlugin` recorded the
7
154
  URL and method of a request but not its body, so every POST/GraphQL endpoint (robota.ua lists vacancies via
8
155
  `POST dracula.robota.ua/?q=getPublishedVacanciesList`) was unusable: a GET of the same URL returns 400.
@@ -11,7 +158,7 @@
11
158
  POSTed to one `/graphql` URL shared a key and only the last body survived. The key now includes method and
12
159
  request body.
13
160
  - **JSON family by default.** The capture allow-list matched exact MIME types only; `application/graphql-
14
- response+json`, `problem+json`, `vnd.api+json`, `text/json` were dropped. Without an explicit
161
+ response+json`, `problem+json`, `vnd.api+json`, `text/json` were dropped. Without an explicit
15
162
  `capture_content_types` every `*/json` and `*+json` type is captured (RFC 6839). Default `max_body_kb` 64 → 256
16
163
  (listing APIs routinely exceed 64 KB).
17
164
  - **Shadow DOM was invisible.** `page.content()` serializes only light DOM: content of open shadow roots (web
@@ -23,7 +170,8 @@
23
170
  - `tests/unit/domain/entities/test_graph_new_features.py` still expected the `DeprecationWarning` that
24
171
  `export_nodes()` stopped emitting in 4aab7525; it now asserts the opposite.
25
172
 
26
- ### Fixed (real-site audit, round 2 — `docs/REAL_SITE_TESTS.md` §1.2)
173
+ ### Fixed (real-site tests)
174
+
27
175
  - **Ctrl+C during `crawl()` hung until the crawl-level timeout** (28 s with `timeout=25`; forever without
28
176
  one). `asyncio.wait_for` in Python 3.11 swallows the outer cancellation when the awaited coroutine finishes
29
177
  at the same moment, so some workers survived `KeyboardInterrupt` and kept crawling. `NodeScanner` and the
@@ -45,19 +193,17 @@
45
193
  — 23 pytest user scenarios on Wikipedia / robota.ua / GitHub / example.com, each hard-capped at 40 s.
46
194
 
47
195
  ### Changed
48
- - **Engine contract verified end-to-end** (audit `06_browser_engine.md` §4, now closed). `tests/transport/
49
- test_engine_contract.py` drives the real `PlaywrightDriver.fetch()` through three engines registered via the
196
+
197
+ - **Engine contract verified end-to-end**. `tests/transport/
198
+ test_engine_contract.py` drives the real `PlaywrightDriver.fetch()` through three engines registered via the
50
199
  public `register_engine()` on a recording fake SDK: the call sequence is byte-for-byte identical across
51
200
  engines, stealth is injected iff `provides_stealth is False`, and `executable_path`/`ignore_default_args`/
52
201
  launch args come only from the engine. No runtime code changed.
53
- - **`LexborDocument.clean_text()` no longer re-parses the page** (audit `01_parser_audit.md` §4 "parse once",
54
- `00_interim_analysis.md` §3.4). To strip `script/style/noscript/svg/head` without mutating the shared tree it
202
+ - **`LexborDocument.clean_text()` no longer re-parses the page**. To strip `script/style/noscript/svg/head` without mutating the shared tree it
55
203
  serialised the document and built a second `LexborHTMLParser` — i.e. every page with the default
56
204
  `TextExtractorPlugin` was parsed twice (`count_parses.py`: 2.0 → **1.0** per page; with `MarkdownPlugin`
57
- 1 parse + 1 clone). It now uses `tree.clone()`. `docs/audit/benchmarks/count_parses.py` works without
58
- bs4/lxml and counts `generate_from_tree` clones.
59
- - **One `ContentDecoder` for every HTTP tier** (audit `docs/audit/01_parser_audit.md` §5 "no parser accepts
60
- bytes"). `AsyncDriver` (aiohttp) and `AsyncCurlCffiDriver` (curl_cffi) no longer rely on the client's own
205
+ 1 parse + 1 clone). It now uses `tree.clone()`.
206
+ - **One `ContentDecoder` for every HTTP tier**. `AsyncDriver` (aiohttp) and `AsyncCurlCffiDriver` (curl_cffi) no longer rely on the client's own
61
207
  `response.text`/`.text()`: they read the raw body and decode it through `ContentDecoder`
62
208
  (`Content-Type` charset → `<meta charset>` → BOM → chardet → strict utf-8). Pages whose charset is only
63
209
  declared in `<meta>` (e.g. windows-1251 without a header) were previously decoded as utf-8 by aiohttp,
@@ -68,7 +214,7 @@
68
214
  (Playwright `page.content()`, nodriver `tab.get_content()`) already return `str`. Contract test:
69
215
  `tests/transport/test_html_is_str_contract.py` (driver matrix returns `str`; cp1251 via meta/header;
70
216
  binary → error).
71
- - **Lazy public facade** (audit `docs/audit/04_dependency_map.md` §5 п.3). `graph_crawler/__init__.py` now
217
+ - **Lazy public facade**. `graph_crawler/__init__.py` now
72
218
  eagerly imports only `crawl`, `async_crawl`, `Graph`, `Node`, `save_graph`, `load_graph`,
73
219
  `BaseStopCondition`; every other name in `__all__` (drivers, storages, exceptions, settings, views,
74
220
  celery tasks, …) resolves on first access via module `__getattr__` and is cached. `aiohttp` is no longer
@@ -76,7 +222,7 @@
76
222
  (`psutil.cpu_percent(interval=0.1)` blocked import for 100 ms for a DEBUG line). Import time here:
77
223
  470 ms → 228 ms. Optional names (`PlaywrightDriver`, `celery`, `EasyDistributedCrawler`, …) still return
78
224
  `None` without their extra.
79
- - **Escalation re-probe replays the solving tier's User-Agent** (audit `03_escalation.md` §3/§6).
225
+ - **Escalation re-probe replays the solving tier's User-Agent**.
80
226
  Playwright and nodriver now export `navigator.userAgent` as `extras["user_agent"]`;
81
227
  `SessionState.absorb` records it (pinned once the session is solved, like `impersonate`/`proxy`) and
82
228
  `EscalatingDriver._fetch_tier` sends it as `extra_headers={"User-Agent": …}` to HTTP/TLS tiers together
@@ -84,7 +230,8 @@
84
230
  default UA was rejected and the host never descended from the browser tier.
85
231
 
86
232
  ### Removed
87
- - **`NodePluginContext.parser`** (audit `01_parser_audit.md` §4 "parse once"). Node plugins now read the
233
+
234
+ - **`NodePluginContext.parser`**. Node plugins now read the
88
235
  single Lexbor tree through the `HTMLDocument`/`HTMLNode` protocols: `LinkExtractorPlugin`
89
236
  (`doc.css("a[href]")`, `doc.base_href()`), `MetadataExtractorPlugin`, `TextExtractorPlugin`
90
237
  (`doc.clean_text()`, new in the protocol), `StructuredDataPlugin` (OG/Twitter/Microdata/RDFa parsers take
@@ -94,16 +241,17 @@
94
241
  `None`) for valueless boolean attributes such as `itemscope`, so nested Microdata/RDFa items are now
95
242
  detected; `css()`/`css_first()` return descendants only (selectolax included the node itself, which made
96
243
  Microdata recurse to `max_nesting_depth`).
97
- - **`PooledPlaywrightDriver` legacy batch path** (audit `06_browser_engine.md` §3): `batch_legacy.py`
244
+ - **`PooledPlaywrightDriver` legacy batch path**: `batch_legacy.py`
98
245
  (`LegacyBatch._fetch_batch_legacy`, `_fetch_chain`) is gone. `persistent_browser=False` keeps its
99
246
  "launch/stop per batch" semantics through the pool path: after a batch, once no fetch is in flight, the
100
247
  pool and the engine session are closed (`_close_pool()` + `_stop_persistent_session()`, shared with
101
248
  `close()`). Dead `self.playwright`/`self.browsers` cleanup removed from `close()`.
102
- - **Escalation 6.0 (audit `docs/audit/03_escalation.md`, Vector 2).** `classify_block()` rewritten to the
249
+ - **Escalation.** `classify_block()` rewritten to the
103
250
  6.0 decision table: challenge/captcha vendor headers first, then unambiguous body captcha/challenge
104
251
  markers (ahead of vendor hard-block headers), then short/blocking-page widgets, status-based verdicts
105
- and empty-2xx. A bare `401/403/405` with no vendor markers is no longer a block. The classifier corpus
106
- (`docs/audit/benchmarks/classifier_corpus.py`) goes 25/37 → **37/37**.
252
+ and empty-2xx. A bare `401/403/405` with no vendor markers is no longer a block. On the classifier corpus
253
+ it goes 25/37 → **37/37**.
254
+
107
255
  - `needs_browser` heuristic (SPA shell: little visible text + SPA root / `<noscript>` JS hint / no
108
256
  `<a href>`) is now the default `EscalationPolicy.needs_browser`; on a clean HTTP/TLS response it jumps
109
257
  straight to the first browser tier (`escalation_reason="needs_browser"`).
@@ -119,7 +267,7 @@
119
267
  - New Prometheus metrics `crawl_fetch_duration_seconds{channel,engine}`, `crawl_escalations_total`,
120
268
  `crawl_escalation_deadline_hits_total`.
121
269
 
122
- - **HTML parsing: Lexbor (selectolax) is the only core parser** (audit `docs/audit/01_parser_audit.md`).
270
+ - **HTML parsing: Lexbor (selectolax) is the only core parser**.
123
271
  `beautifulsoup4`, `lxml`, `lxml_html_clean` removed from `[project].dependencies`;
124
272
  `BeautifulSoupAdapter`, `LxmlAdapter`, `ScrapyAdapter` deleted. New `HTMLDocument` / `HTMLNode`
125
273
  protocols in `domain/value_objects/html_document.py`, implemented by `LexborDocument` in
@@ -136,16 +284,16 @@
136
284
  `MarkdownPlugin` feeds the already-parsed Lexbor tree to html2md-clean 0.5 (`generate_from_tree`) —
137
285
  one parse per page instead of 2–4. `import graph_crawler` no longer loads bs4/lxml.
138
286
 
139
- - **Dependency map 6.0** (audit `docs/audit/04_dependency_map.md`). Core is now 10 packages: `requests`,
287
+ - **Dependency map**. Core is now 10 packages: `requests`,
140
288
  `httpx`, `pydantic-settings`, `pybloom-live`, `fastapi`, `tenacity`, `stop-words`, `tiktoken`,
141
289
  `markdown-it-py` removed from `[project].dependencies`. New extras: `browser` (`playwright` kept as alias),
142
290
  `server` (fastapi/uvicorn/websockets), `text` (tiktoken/stop-words), `captcha`; `markdown` also pulls
143
291
  `markdown-it-py`; `all` is recursive. Extras `cloudscraper` and `undetected-sync` dropped.
144
292
  Extra-gated imports are lazy and fail fast with `requires extra '<name>'`; `transport.PlaywrightDriver/
145
- RequestsDriver/CloudscraperDriver/RequestsSessionAdapter` are lazy re-exports (ImportError instead of `None`).
293
+ RequestsDriver/CloudscraperDriver/RequestsSessionAdapter` are lazy re-exports (ImportError instead of `None`).
146
294
  `CrawlerSettings` keeps `GC_*`/`__`/`.env` without pydantic-settings; `CrawlerConfig` is a plain `BaseModel`.
147
295
 
148
- - **Transport eradication 6.0** (audit `docs/audit/02_codebase_audit.md`). Deleted: sync drivers
296
+ - **Transport cleanup**. Deleted: sync drivers
149
297
  (`RequestsDriver`, `CloudscraperDriver`, `UndetectedChromeDriver`, `BotasaurusDriver`, `BaseSyncDriver`),
150
298
  `AsyncDriverHTTP2` (httpx), `AsyncCloudscraperDriver`, `tls_client.py`, `StealthHTTPDriver`,
151
299
  `session_manager/session_adapters/session_protocol`, `shared/utils/captcha_bypass.py`,
@@ -159,7 +307,7 @@
159
307
  `http_client=` is deprecated and ignored). `BloomFilter` is pure-python (xxhash) — `pybloom-live`
160
308
  no longer needed.
161
309
  `import graph_crawler` no longer loads any extra (guarded by `tests/unit/test_import_surface.py`), 3.8 s → 1.7 s here.
162
- Follow-up (02 §6 close-out): `shared/constants.py` no longer imports aiohttp — `br`/`zstd` are advertised
310
+ `shared/constants.py` no longer imports aiohttp — `br`/`zstd` are advertised
163
311
  by `transport.compression.runtime_browser_headers()` only when the runtime can decode them;
164
312
  one driver registry — `application.services.driver_factory` delegates to
165
313
  `transport.factory.DriverFactory` (`register_factory`, `registered_names`); `SitemapParser(driver=)`
@@ -167,20 +315,20 @@
167
315
  driver, aiohttp is used directly only for binary `.gz`; `tests/transport/test_all_drivers_report_redirects.py`
168
316
  is an `IDriver` acceptance matrix (aiohttp / tls / fake playwright / fake nodriver / escalating).
169
317
 
170
- - **Escalation (audit `docs/audit/03_escalation.md` §6/§7)**: `EscalationPolicy.reprobe_interval` (5 min) —
318
+ - **Escalation**: `EscalationPolicy.reprobe_interval` (5 min) —
171
319
  one request per interval re-probes tier 0 for a host remembered on a higher tier (no herd of browser
172
320
  re-escalations after TTL); `EscalationPolicy.max_hosts` (10k) — `HostTierMemory` is an LRU and drops
173
- per-host locks together with entries. In strict mode (`GC_ENGINE_STRICT`) an `ImportError` raised *during*
321
+ per-host locks together with entries. In strict mode (`GC_ENGINE_STRICT`) an `ImportError` raised _during_
174
322
  `fetch` (lazy browser start without the SDK) now propagates instead of being turned into a network error.
175
323
 
176
- - **`extras` contract persisted (audit `docs/audit/05_migration_guide.md` §4).** New
324
+ - **`extras` contract persisted.** New
177
325
  `FetchResponse.fetch_provenance()` / `FETCH_EXTRAS_KEYS`: a JSON-safe slice
178
326
  (`bot_block_status, channel, driver, engine, escalations` + `escalation_reason`; cookies/impersonate dropped).
179
327
  `NodeScanner` stores it in `node.metadata["fetch"]` for every response, including blocked/error ones, so it
180
328
  survives JSON/SQLite/MongoDB/PostgreSQL storage. Contract test `tests/transport/test_extras_contract.py`.
181
- - `docs/audit/05_migration_guide.md` — 5.2 → 6.0 migration guide (extras, drivers, parser, settings, sitemap, checklist).
182
329
 
183
330
  ### Fixed
331
+
184
332
  - Transport `extras` (`bot_block_status`, tier, escalations) were dropped after fetch and never reached the
185
333
  node or storage; for blocked pages nothing was transferred at all.
186
334
  - `SmartPageFinder` read `html_tree.text` as a property; with Lexbor it is a method — page text was
@@ -192,6 +340,7 @@
192
340
  ## 5.2.0 — 2026-06
193
341
 
194
342
  ### Added
343
+
195
344
  - `infrastructure/transport/escalation.py`: `classify_block()` (Cloudflare / Akamai / DataDome /
196
345
  PerimeterX / Incapsula markers, 403/429/503, empty 200), `EscalationPolicy`, `EscalationTier`,
197
346
  `EscalatingDriver` — chain http → tls → browser → undetected with `max_tier`.
@@ -213,6 +362,7 @@
213
362
  - `README.md`, `CHANGELOG.md`.
214
363
 
215
364
  ### Changed
365
+
216
366
  - `normalize_protocol` defaults to `False`: `http://` and `https://` are different URLs.
217
367
  The crawler follows the real redirect instead of rewriting the scheme itself, so
218
368
  http-only hosts stay reachable. Enable it explicitly to merge both schemes on dedup.
@@ -227,6 +377,7 @@
227
377
  - Project URLs point to the GitLab repository.
228
378
 
229
379
  ### Fixed
380
+
230
381
  - `pooled_driver`: bounded walk over `redirected_from` (infinite loop on objects without a
231
382
  `None` terminator); cookies are no longer added twice to a fresh browser.
232
383
  - Tests synced with the code: redirect aliases, `html2md-clean` 0.4.0 API, `crawl_coordinator`
@@ -234,6 +385,7 @@
234
385
  CLI allow-listed for `print`. Playwright fake tests skip when `playwright` is not installed.
235
386
 
236
387
  ### Removed
388
+
237
389
  - `tests/345.py`, `tests/ДОДАТКОВІ_ТЕСТИ.py`, `tests/manual_4a_games_test.py`, `tests/google_maps_results.json`.
238
390
  - `EXTRACTION_AUDIT.md` and other analysis / plan documents that described work, not the code.
239
391
  - `graph_crawler/data/ARCHITECTURE.md`, `MIGRATION_50_STEPS.md`, `REFACTORING_PLAN.md`, `mkdocs.yml`
@@ -1,14 +1,11 @@
1
1
  include README.md
2
2
  include LICENSE
3
3
  include CHANGELOG.md
4
- include CODE_STYLE.md
5
4
  include pyproject.toml
6
5
  include graph_crawler/py.typed
7
6
 
8
7
  recursive-include graph_crawler/native *.pyx
9
- recursive-include graph_crawler/native *.pxd
10
8
  include graph_crawler/native/setup.py
11
- include graph_crawler/native/README.md
12
9
 
13
10
  prune tests
14
11
  global-exclude __pycache__