pulsecrawl 0.1.0__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 (313) hide show
  1. pulsecrawl-0.1.0/CHANGELOG.md +85 -0
  2. pulsecrawl-0.1.0/CITATION.cff +20 -0
  3. pulsecrawl-0.1.0/Dockerfile +24 -0
  4. pulsecrawl-0.1.0/Dockerfile.dockerignore +15 -0
  5. pulsecrawl-0.1.0/LICENSE +661 -0
  6. pulsecrawl-0.1.0/MANIFEST.in +9 -0
  7. pulsecrawl-0.1.0/PKG-INFO +104 -0
  8. pulsecrawl-0.1.0/PYPI_README.md +52 -0
  9. pulsecrawl-0.1.0/README.md +342 -0
  10. pulsecrawl-0.1.0/_build_backend.py +56 -0
  11. pulsecrawl-0.1.0/docs/AgentTemplateAuthoring.md +149 -0
  12. pulsecrawl-0.1.0/docs/PackageArchitecture.md +161 -0
  13. pulsecrawl-0.1.0/docs/PyPI.md +167 -0
  14. pulsecrawl-0.1.0/docs/ResponsibleCrawling.md +45 -0
  15. pulsecrawl-0.1.0/docs/ThreatModel.md +114 -0
  16. pulsecrawl-0.1.0/examples/README.md +159 -0
  17. pulsecrawl-0.1.0/examples/__init__.py +1 -0
  18. pulsecrawl-0.1.0/examples/agent_template_authoring.py +53 -0
  19. pulsecrawl-0.1.0/examples/chihuahua_news.py +17 -0
  20. pulsecrawl-0.1.0/examples/hosted_cases.py +15 -0
  21. pulsecrawl-0.1.0/examples/hosted_quickstart.py +98 -0
  22. pulsecrawl-0.1.0/examples/jobsearch/__init__.py +5 -0
  23. pulsecrawl-0.1.0/examples/jobsearch/hosted.py +12 -0
  24. pulsecrawl-0.1.0/examples/jobsearch/local.py +12 -0
  25. pulsecrawl-0.1.0/examples/jobsearch/recipes.py +364 -0
  26. pulsecrawl-0.1.0/examples/jobsearch/validate_recipes.py +21 -0
  27. pulsecrawl-0.1.0/examples/local_cases.py +19 -0
  28. pulsecrawl-0.1.0/examples/local_quickstart.py +62 -0
  29. pulsecrawl-0.1.0/examples/marketplace/__init__.py +1 -0
  30. pulsecrawl-0.1.0/examples/marketplace/clothes/__init__.py +1 -0
  31. pulsecrawl-0.1.0/examples/marketplace/clothes/hosted.py +12 -0
  32. pulsecrawl-0.1.0/examples/marketplace/clothes/local.py +12 -0
  33. pulsecrawl-0.1.0/examples/marketplace/clothes/recipes.py +337 -0
  34. pulsecrawl-0.1.0/examples/marketplace/clothes/validate_recipes.py +21 -0
  35. pulsecrawl-0.1.0/examples/marketplace/mro/__init__.py +1 -0
  36. pulsecrawl-0.1.0/examples/marketplace/mro/hosted.py +12 -0
  37. pulsecrawl-0.1.0/examples/marketplace/mro/local.py +12 -0
  38. pulsecrawl-0.1.0/examples/marketplace/mro/recipes.py +442 -0
  39. pulsecrawl-0.1.0/examples/marketplace/other_industries/__init__.py +1 -0
  40. pulsecrawl-0.1.0/examples/marketplace/other_industries/hosted.py +12 -0
  41. pulsecrawl-0.1.0/examples/marketplace/other_industries/local.py +12 -0
  42. pulsecrawl-0.1.0/examples/marketplace/other_industries/recipes.py +468 -0
  43. pulsecrawl-0.1.0/examples/marketplace/other_industries/validate_recipes.py +21 -0
  44. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/README.md +82 -0
  45. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/__init__.py +1 -0
  46. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/audit.py +140 -0
  47. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/controlled-demo-summary.json +231 -0
  48. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/controlled.py +117 -0
  49. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/live-sources.json +1559 -0
  50. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/live.py +484 -0
  51. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/local.py +196 -0
  52. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/recipes.py +248 -0
  53. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/research.md +85 -0
  54. pulsecrawl-0.1.0/examples/marketplace/premium_commerce/robots-audit-2026-10-11.json +141 -0
  55. pulsecrawl-0.1.0/examples/marketplace/supermarkets/__init__.py +1 -0
  56. pulsecrawl-0.1.0/examples/marketplace/supermarkets/hosted.py +12 -0
  57. pulsecrawl-0.1.0/examples/marketplace/supermarkets/local.py +12 -0
  58. pulsecrawl-0.1.0/examples/marketplace/supermarkets/recipes.py +405 -0
  59. pulsecrawl-0.1.0/examples/news/__init__.py +1 -0
  60. pulsecrawl-0.1.0/examples/news/chihuahua/__init__.py +1 -0
  61. pulsecrawl-0.1.0/examples/news/chihuahua/hosted.py +12 -0
  62. pulsecrawl-0.1.0/examples/news/chihuahua/local.py +12 -0
  63. pulsecrawl-0.1.0/examples/news/chihuahua/recipes.py +432 -0
  64. pulsecrawl-0.1.0/examples/news/mexico/__init__.py +1 -0
  65. pulsecrawl-0.1.0/examples/news/mexico/hosted.py +12 -0
  66. pulsecrawl-0.1.0/examples/news/mexico/local.py +12 -0
  67. pulsecrawl-0.1.0/examples/news/mexico/recipes.py +458 -0
  68. pulsecrawl-0.1.0/examples/news/stocks/__init__.py +1 -0
  69. pulsecrawl-0.1.0/examples/news/stocks/hosted.py +12 -0
  70. pulsecrawl-0.1.0/examples/news/stocks/local.py +12 -0
  71. pulsecrawl-0.1.0/examples/news/stocks/recipes.py +432 -0
  72. pulsecrawl-0.1.0/examples/shared/__init__.py +1 -0
  73. pulsecrawl-0.1.0/examples/shared/catalog.py +128 -0
  74. pulsecrawl-0.1.0/examples/shared/cli.py +76 -0
  75. pulsecrawl-0.1.0/examples/shared/fixtures.py +41 -0
  76. pulsecrawl-0.1.0/examples/shared/hosted.py +196 -0
  77. pulsecrawl-0.1.0/examples/shared/local.py +222 -0
  78. pulsecrawl-0.1.0/examples/shared/recipes.py +63 -0
  79. pulsecrawl-0.1.0/examples/shared/safety.py +18 -0
  80. pulsecrawl-0.1.0/examples/shared/validation.py +196 -0
  81. pulsecrawl-0.1.0/examples/workspace_quickstart.py +61 -0
  82. pulsecrawl-0.1.0/pyproject.toml +88 -0
  83. pulsecrawl-0.1.0/requirements-workspace.txt +25 -0
  84. pulsecrawl-0.1.0/scripts/check-distributions.py +232 -0
  85. pulsecrawl-0.1.0/scripts/check-doc-links.py +73 -0
  86. pulsecrawl-0.1.0/scripts/check-release-ci.py +96 -0
  87. pulsecrawl-0.1.0/scripts/check-release.py +72 -0
  88. pulsecrawl-0.1.0/scripts/check-secrets.py +53 -0
  89. pulsecrawl-0.1.0/scripts/export-oss.py +281 -0
  90. pulsecrawl-0.1.0/scripts/smoke-distribution.py +82 -0
  91. pulsecrawl-0.1.0/setup.cfg +4 -0
  92. pulsecrawl-0.1.0/src/pulsecrawl/__init__.py +72 -0
  93. pulsecrawl-0.1.0/src/pulsecrawl/adapters/__init__.py +1 -0
  94. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/__init__.py +1 -0
  95. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/cli/__init__.py +1 -0
  96. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/cli/main.py +385 -0
  97. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/cli/parser.py +220 -0
  98. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/__init__.py +1 -0
  99. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/ephemeral.py +142 -0
  100. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/intent.py +103 -0
  101. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/local.py +175 -0
  102. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/resources.py +15 -0
  103. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/schedules.py +27 -0
  104. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/tasks.py +25 -0
  105. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/python/workspace.py +71 -0
  106. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/scheduler/__init__.py +1 -0
  107. pulsecrawl-0.1.0/src/pulsecrawl/adapters/inbound/scheduler/process.py +62 -0
  108. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/__init__.py +1 -0
  109. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/credentials/__init__.py +1 -0
  110. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/credentials/profiles.py +37 -0
  111. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/cron/__init__.py +1 -0
  112. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/cron/calculator.py +54 -0
  113. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/duckdb/__init__.py +1 -0
  114. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/duckdb/local_result.py +112 -0
  115. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/duckdb/query.py +271 -0
  116. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/__init__.py +1 -0
  117. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/atomic.py +119 -0
  118. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/backup_io.py +34 -0
  119. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/backups.py +196 -0
  120. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/catalogs.py +46 -0
  121. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/dataset_files.py +83 -0
  122. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/doctor.py +106 -0
  123. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/downloads.py +140 -0
  124. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/drafts.py +51 -0
  125. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/legacy_execution.py +156 -0
  126. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/manifests.py +228 -0
  127. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/pages.py +32 -0
  128. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/publication.py +95 -0
  129. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/retention.py +120 -0
  130. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/windows_publication.py +66 -0
  131. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/filesystem/workspace.py +68 -0
  132. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/__init__.py +1 -0
  133. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/extraction/__init__.py +38 -0
  134. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/extraction/css.py +69 -0
  135. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/extraction/jsonld.py +123 -0
  136. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/extraction/meta.py +56 -0
  137. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/inspection.py +161 -0
  138. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/html/links.py +25 -0
  139. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/__init__.py +1 -0
  140. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/artifacts.py +104 -0
  141. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/client.py +194 -0
  142. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/constants.py +5 -0
  143. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/fetcher.py +273 -0
  144. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/__init__.py +1 -0
  145. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/base.py +6 -0
  146. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/datasets.py +114 -0
  147. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/executions.py +110 -0
  148. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/fields.py +24 -0
  149. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/jobs.py +119 -0
  150. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/organizations.py +34 -0
  151. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/schedules.py +29 -0
  152. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/schemas.py +26 -0
  153. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/templates.py +43 -0
  154. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/resources/urls.py +27 -0
  155. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/scope.py +31 -0
  156. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/http/workspace.py +40 -0
  157. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/memory/__init__.py +1 -0
  158. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/memory/crawling.py +215 -0
  159. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/memory/records.py +12 -0
  160. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/parquet/__init__.py +1 -0
  161. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/parquet/compaction.py +120 -0
  162. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/parquet/publication.py +151 -0
  163. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/__init__.py +1 -0
  164. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/clock.py +36 -0
  165. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/crawling.py +305 -0
  166. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/definitions.py +53 -0
  167. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/executions.py +223 -0
  168. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/health.py +46 -0
  169. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/history.py +67 -0
  170. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/leases.py +108 -0
  171. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/maintenance_lease.py +77 -0
  172. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/records.py +211 -0
  173. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/scheduler_lease.py +46 -0
  174. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/schedules.py +121 -0
  175. pulsecrawl-0.1.0/src/pulsecrawl/adapters/outbound/sqlite/state.py +365 -0
  176. pulsecrawl-0.1.0/src/pulsecrawl/application/__init__.py +1 -0
  177. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/__init__.py +1 -0
  178. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/admission.py +160 -0
  179. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/dispatch.py +152 -0
  180. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/engine.py +125 -0
  181. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/pages.py +291 -0
  182. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/records.py +50 -0
  183. pulsecrawl-0.1.0/src/pulsecrawl/application/crawling/robots.py +124 -0
  184. pulsecrawl-0.1.0/src/pulsecrawl/application/datasets/__init__.py +1 -0
  185. pulsecrawl-0.1.0/src/pulsecrawl/application/datasets/publication.py +24 -0
  186. pulsecrawl-0.1.0/src/pulsecrawl/application/datasets/service.py +169 -0
  187. pulsecrawl-0.1.0/src/pulsecrawl/application/executions/__init__.py +1 -0
  188. pulsecrawl-0.1.0/src/pulsecrawl/application/executions/service.py +310 -0
  189. pulsecrawl-0.1.0/src/pulsecrawl/application/ports/__init__.py +1 -0
  190. pulsecrawl-0.1.0/src/pulsecrawl/application/ports/cloud.py +23 -0
  191. pulsecrawl-0.1.0/src/pulsecrawl/application/ports/crawling.py +150 -0
  192. pulsecrawl-0.1.0/src/pulsecrawl/application/ports/workspace.py +98 -0
  193. pulsecrawl-0.1.0/src/pulsecrawl/application/scheduling/__init__.py +1 -0
  194. pulsecrawl-0.1.0/src/pulsecrawl/application/scheduling/service.py +126 -0
  195. pulsecrawl-0.1.0/src/pulsecrawl/application/synchronization/__init__.py +1 -0
  196. pulsecrawl-0.1.0/src/pulsecrawl/application/synchronization/profiles.py +110 -0
  197. pulsecrawl-0.1.0/src/pulsecrawl/application/synchronization/service.py +231 -0
  198. pulsecrawl-0.1.0/src/pulsecrawl/application/telemetry.py +46 -0
  199. pulsecrawl-0.1.0/src/pulsecrawl/application/templates/__init__.py +1 -0
  200. pulsecrawl-0.1.0/src/pulsecrawl/application/templates/authoring.py +398 -0
  201. pulsecrawl-0.1.0/src/pulsecrawl/application/templates/resolution.py +91 -0
  202. pulsecrawl-0.1.0/src/pulsecrawl/application/templates/validation.py +246 -0
  203. pulsecrawl-0.1.0/src/pulsecrawl/application/time.py +11 -0
  204. pulsecrawl-0.1.0/src/pulsecrawl/application/workspace/__init__.py +1 -0
  205. pulsecrawl-0.1.0/src/pulsecrawl/application/workspace/catalogs.py +154 -0
  206. pulsecrawl-0.1.0/src/pulsecrawl/application/workspace/jobs.py +240 -0
  207. pulsecrawl-0.1.0/src/pulsecrawl/application/workspace/maintenance.py +19 -0
  208. pulsecrawl-0.1.0/src/pulsecrawl/application/workspace/validation.py +117 -0
  209. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/__init__.py +1 -0
  210. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/authoring.py +46 -0
  211. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/cloud.py +124 -0
  212. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/crawling.py +135 -0
  213. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/datasets.py +107 -0
  214. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/ephemeral.py +46 -0
  215. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/maintenance.py +46 -0
  216. pulsecrawl-0.1.0/src/pulsecrawl/bootstrap/workspace.py +134 -0
  217. pulsecrawl-0.1.0/src/pulsecrawl/cli.py +9 -0
  218. pulsecrawl-0.1.0/src/pulsecrawl/client.py +8 -0
  219. pulsecrawl-0.1.0/src/pulsecrawl/cloud.py +6 -0
  220. pulsecrawl-0.1.0/src/pulsecrawl/core/__init__.py +37 -0
  221. pulsecrawl-0.1.0/src/pulsecrawl/core/adapters/__init__.py +0 -0
  222. pulsecrawl-0.1.0/src/pulsecrawl/core/adapters/aiohttp_fetcher.py +8 -0
  223. pulsecrawl-0.1.0/src/pulsecrawl/core/adapters/memory.py +8 -0
  224. pulsecrawl-0.1.0/src/pulsecrawl/core/coerce.py +8 -0
  225. pulsecrawl-0.1.0/src/pulsecrawl/core/engine.py +15 -0
  226. pulsecrawl-0.1.0/src/pulsecrawl/core/extraction/__init__.py +6 -0
  227. pulsecrawl-0.1.0/src/pulsecrawl/core/extraction/css.py +8 -0
  228. pulsecrawl-0.1.0/src/pulsecrawl/core/extraction/jsonld.py +8 -0
  229. pulsecrawl-0.1.0/src/pulsecrawl/core/extraction/meta.py +8 -0
  230. pulsecrawl-0.1.0/src/pulsecrawl/core/local.py +8 -0
  231. pulsecrawl-0.1.0/src/pulsecrawl/core/model.py +13 -0
  232. pulsecrawl-0.1.0/src/pulsecrawl/core/politeness.py +8 -0
  233. pulsecrawl-0.1.0/src/pulsecrawl/core/ports.py +8 -0
  234. pulsecrawl-0.1.0/src/pulsecrawl/core/robots.py +8 -0
  235. pulsecrawl-0.1.0/src/pulsecrawl/core/urls.py +4 -0
  236. pulsecrawl-0.1.0/src/pulsecrawl/domain/__init__.py +1 -0
  237. pulsecrawl-0.1.0/src/pulsecrawl/domain/crawling/__init__.py +1 -0
  238. pulsecrawl-0.1.0/src/pulsecrawl/domain/crawling/model.py +359 -0
  239. pulsecrawl-0.1.0/src/pulsecrawl/domain/crawling/politeness.py +19 -0
  240. pulsecrawl-0.1.0/src/pulsecrawl/domain/crawling/retry.py +25 -0
  241. pulsecrawl-0.1.0/src/pulsecrawl/domain/crawling/urls.py +134 -0
  242. pulsecrawl-0.1.0/src/pulsecrawl/domain/datasets/__init__.py +1 -0
  243. pulsecrawl-0.1.0/src/pulsecrawl/domain/datasets/identity.py +43 -0
  244. pulsecrawl-0.1.0/src/pulsecrawl/domain/errors.py +46 -0
  245. pulsecrawl-0.1.0/src/pulsecrawl/domain/executions/__init__.py +1 -0
  246. pulsecrawl-0.1.0/src/pulsecrawl/domain/executions/lifecycle.py +73 -0
  247. pulsecrawl-0.1.0/src/pulsecrawl/domain/scheduling/__init__.py +1 -0
  248. pulsecrawl-0.1.0/src/pulsecrawl/domain/scheduling/identity.py +8 -0
  249. pulsecrawl-0.1.0/src/pulsecrawl/domain/synchronization/__init__.py +1 -0
  250. pulsecrawl-0.1.0/src/pulsecrawl/domain/synchronization/planner.py +325 -0
  251. pulsecrawl-0.1.0/src/pulsecrawl/domain/synchronization/profiles.py +11 -0
  252. pulsecrawl-0.1.0/src/pulsecrawl/domain/templates/__init__.py +1 -0
  253. pulsecrawl-0.1.0/src/pulsecrawl/domain/templates/authoring.py +188 -0
  254. pulsecrawl-0.1.0/src/pulsecrawl/domain/templates/coercion.py +71 -0
  255. pulsecrawl-0.1.0/src/pulsecrawl/domain/templates/selection.py +44 -0
  256. pulsecrawl-0.1.0/src/pulsecrawl/domain/workspace/__init__.py +1 -0
  257. pulsecrawl-0.1.0/src/pulsecrawl/domain/workspace/definitions.py +136 -0
  258. pulsecrawl-0.1.0/src/pulsecrawl/errors.py +6 -0
  259. pulsecrawl-0.1.0/src/pulsecrawl/local.py +8 -0
  260. pulsecrawl-0.1.0/src/pulsecrawl/local_dataset.py +6 -0
  261. pulsecrawl-0.1.0/src/pulsecrawl/local_scheduler.py +28 -0
  262. pulsecrawl-0.1.0/src/pulsecrawl/observability.py +6 -0
  263. pulsecrawl-0.1.0/src/pulsecrawl/sqlite_state.py +10 -0
  264. pulsecrawl-0.1.0/src/pulsecrawl/template_authoring.py +6 -0
  265. pulsecrawl-0.1.0/src/pulsecrawl/workspace.py +8 -0
  266. pulsecrawl-0.1.0/src/pulsecrawl/workspace_maintenance.py +6 -0
  267. pulsecrawl-0.1.0/src/pulsecrawl/workspace_runtime.py +8 -0
  268. pulsecrawl-0.1.0/src/pulsecrawl.egg-info/PKG-INFO +104 -0
  269. pulsecrawl-0.1.0/src/pulsecrawl.egg-info/SOURCES.txt +311 -0
  270. pulsecrawl-0.1.0/src/pulsecrawl.egg-info/dependency_links.txt +1 -0
  271. pulsecrawl-0.1.0/src/pulsecrawl.egg-info/entry_points.txt +2 -0
  272. pulsecrawl-0.1.0/src/pulsecrawl.egg-info/requires.txt +34 -0
  273. pulsecrawl-0.1.0/src/pulsecrawl.egg-info/top_level.txt +1 -0
  274. pulsecrawl-0.1.0/tests/application/test_execution_service.py +132 -0
  275. pulsecrawl-0.1.0/tests/application/test_publication_order.py +87 -0
  276. pulsecrawl-0.1.0/tests/architecture/test_boundaries.py +170 -0
  277. pulsecrawl-0.1.0/tests/contracts/test_artifact_downloads.py +234 -0
  278. pulsecrawl-0.1.0/tests/contracts/test_backup_publication.py +107 -0
  279. pulsecrawl-0.1.0/tests/contracts/test_crawling_adapters.py +123 -0
  280. pulsecrawl-0.1.0/tests/contracts/test_durable_file_flush.py +53 -0
  281. pulsecrawl-0.1.0/tests/contracts/test_execution_preparation.py +81 -0
  282. pulsecrawl-0.1.0/tests/contracts/test_sqlite_cursor_synchronization.py +51 -0
  283. pulsecrawl-0.1.0/tests/core/__init__.py +0 -0
  284. pulsecrawl-0.1.0/tests/core/conftest.py +123 -0
  285. pulsecrawl-0.1.0/tests/core/test_aiohttp_fetcher.py +370 -0
  286. pulsecrawl-0.1.0/tests/core/test_coerce.py +76 -0
  287. pulsecrawl-0.1.0/tests/core/test_conformance.py +149 -0
  288. pulsecrawl-0.1.0/tests/core/test_engine.py +996 -0
  289. pulsecrawl-0.1.0/tests/core/test_extraction.py +140 -0
  290. pulsecrawl-0.1.0/tests/core/test_local.py +49 -0
  291. pulsecrawl-0.1.0/tests/core/test_model.py +54 -0
  292. pulsecrawl-0.1.0/tests/core/test_robots.py +81 -0
  293. pulsecrawl-0.1.0/tests/core/test_urls.py +126 -0
  294. pulsecrawl-0.1.0/tests/domain/test_lifecycle_policies.py +117 -0
  295. pulsecrawl-0.1.0/tests/test_business_scenarios.py +453 -0
  296. pulsecrawl-0.1.0/tests/test_client.py +537 -0
  297. pulsecrawl-0.1.0/tests/test_cloud.py +528 -0
  298. pulsecrawl-0.1.0/tests/test_container_dependencies.py +34 -0
  299. pulsecrawl-0.1.0/tests/test_dataset_versions.py +144 -0
  300. pulsecrawl-0.1.0/tests/test_example_workflows.py +415 -0
  301. pulsecrawl-0.1.0/tests/test_failure_scenarios.py +525 -0
  302. pulsecrawl-0.1.0/tests/test_hosted_examples.py +165 -0
  303. pulsecrawl-0.1.0/tests/test_live_commerce_example.py +157 -0
  304. pulsecrawl-0.1.0/tests/test_local.py +195 -0
  305. pulsecrawl-0.1.0/tests/test_premium_commerce_example.py +192 -0
  306. pulsecrawl-0.1.0/tests/test_production_validation.py +519 -0
  307. pulsecrawl-0.1.0/tests/test_release_artifacts.py +255 -0
  308. pulsecrawl-0.1.0/tests/test_release_ci.py +86 -0
  309. pulsecrawl-0.1.0/tests/test_soak_validation.py +75 -0
  310. pulsecrawl-0.1.0/tests/test_template_authoring.py +545 -0
  311. pulsecrawl-0.1.0/tests/test_workspace_hardening.py +649 -0
  312. pulsecrawl-0.1.0/tests/test_workspace_runtime.py +763 -0
  313. pulsecrawl-0.1.0/tools/soak_workspace.py +354 -0
@@ -0,0 +1,85 @@
1
+ # Changelog
2
+
3
+ ## Publishing preparation
4
+
5
+ - Serialize shared SQLite cursor reads as well as statement execution.
6
+ - Flush completed output files with Windows-compatible writable descriptors,
7
+ retain directory write/rename exclusion during downloads, and install Windows
8
+ timezone data for scheduling. Profile metadata uses POSIX modes or inherited
9
+ Windows ACLs; credentials remain outside that file on every platform.
10
+ - Generate the CI SBOM outside the checkout so the clean-source release gate
11
+ continues to reject genuine uncommitted source changes.
12
+ - Publish Windows downloads with a same-parent native rename so publication
13
+ does not reopen or unlock a parent protected against reparse mutation.
14
+ - Pin container aiohttp consistently with the package security floor, and test
15
+ container-lock compatibility to catch future dependency drift.
16
+ - Publish downloaded artifacts without clobbering concurrent writers by default;
17
+ pin validated parent directories before creating descendants or staging files.
18
+ - Reject ambiguous, duplicate, and platform-specific artifact paths before writes.
19
+ - Strip personal ownership/PAX metadata from source archives at build time and
20
+ reject identity leaks or personal maintainer metadata in distribution checks.
21
+ - Validate restored workspaces before atomic no-clobber publication; never
22
+ remove or replace a competing backup/restore destination.
23
+ - Explicitly reject multicast and special-purpose crawler destinations across
24
+ supported Python versions, including IPv4-mapped IPv6 addresses.
25
+ - Raise the aiohttp security floor to 3.14.5 and pytest to 9.0.3 (with compatible
26
+ pytest-asyncio); audit minimum dependencies including transitive packages.
27
+ - Use audited setuptools 84 or newer for source builds and backend tests.
28
+ - Require successful full CI on the exact tagged SHA and protected publishing
29
+ environments before allowing an upload.
30
+ - Add a PyPI-specific description, complete source archives, artifact validation,
31
+ and clean-install smoke checks.
32
+ - Prepare manual GitHub OIDC publishing with TestPyPI rehearsal and separate
33
+ production approval. Account, publisher, and environment setup remain launch tasks.
34
+
35
+ All notable changes to the public PulseCrawl package will be recorded here.
36
+ PulseCrawl has not yet published a tagged software release.
37
+
38
+ ## Unreleased
39
+
40
+ ### Added
41
+
42
+ - Import-safe offline memory and workspace quickstarts, shared example
43
+ provisioners, runnable per-category entry points, and offline workflow tests.
44
+ - Recipe validation using saved HTML or explicitly opted-in governed sampling;
45
+ no samples checked is inconclusive rather than a successful validation.
46
+ - `pulsecrawl.core`, a cloud-independent crawl engine with bounded frontiers,
47
+ robots handling, request pacing, redirect reauthorization, response-size
48
+ limits, typed extraction, and explicit private-network opt-in.
49
+ - Top-level `pulsecrawl` APIs for synchronous local crawling and portable workspaces with SQLite
50
+ state, Parquet datasets, embedded DuckDB queries, schedules, compaction,
51
+ template authoring, and an optional explicitly configured hosted client.
52
+ - Native and Docker-based local operation without a required cloud account,
53
+ control plane, Redis deployment, or external database server.
54
+ - An allowlisted public-source exporter, cross-platform Python CI, package
55
+ build validation, dependency auditing, SBOM generation, secret scanning,
56
+ and release automation.
57
+ - GNU Affero General Public License v3.0 licensing, governance, DCO contribution terms, security
58
+ reporting, community standards, support boundaries, and responsible-use
59
+ guidance.
60
+ - Optional OS-keyring cloud profiles and explicit workspace plan/push/run/pull
61
+ commands; no local command contacts cloud implicitly and signed artifact
62
+ downloads never receive the hosted API key.
63
+ - A single `pulsecrawl` distribution containing both the engine and SDK,
64
+ replacing the pre-release split-package layout.
65
+ - Hexagonal internal layers (`domain`, `application`, `adapters`, `bootstrap`),
66
+ focused crawl services, repository and runtime ports, and executable
67
+ dependency-boundary tests. Existing public imports remain compatible.
68
+
69
+ ### Fixed
70
+
71
+ - Concurrent preparation of the same execution ID now returns the original
72
+ execution and reserves lifetime capacity only once. Identity lookup,
73
+ reservation, and insertion share one immediate SQLite transaction.
74
+
75
+ ### Safety and compatibility notes
76
+
77
+ - Network requests reject non-global IPv4 and IPv6 destinations by default,
78
+ including loopback, private, link-local, reserved, and metadata-service
79
+ addresses resolved directly or through DNS.
80
+ - Redirect destinations are returned to the crawl engine for scope, robots,
81
+ and origin-rate authorization before the next request.
82
+ - Hosted SDK clients have no built-in service endpoint and require an explicit
83
+ `base_url` or `PULSECRAWL_BASE_URL`.
84
+ - PulseCrawl is alpha software. Public compatibility begins with the first
85
+ tagged release; no earlier commit is a supported release.
@@ -0,0 +1,20 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use PulseCrawl or its research benchmark, cite this software artifact."
3
+ title: "PulseCrawl: Design and Evaluation of a Framework for Policy-Constrained Recurring Web Crawling and Versioned Structured Datasets"
4
+ type: software
5
+ authors:
6
+ - name: "PulseCrawl contributors"
7
+ version: "0.1.0"
8
+ license: "AGPL-3.0-only"
9
+ abstract: >-
10
+ A framework for policy-constrained recurring web crawling, structured
11
+ extraction, and versioned Parquet datasets, with explicit request policies,
12
+ durable safety invariants, URL-scoped template cascades, and a reproducible
13
+ comparative evaluation.
14
+ keywords:
15
+ - web crawling
16
+ - web scraping
17
+ - distributed systems
18
+ - data lake
19
+ - information extraction
20
+ repository-code: "https://github.com/pulsecrawl/PulseCrawl"
@@ -0,0 +1,24 @@
1
+ FROM python:3.13.16-slim-bookworm@sha256:55e31428469cbd0e20d52b2b514e8ec20ca4742254ed2b9dbe0f0b2d39cc2d65
2
+
3
+ LABEL org.opencontainers.image.source="https://github.com/pulsecrawl/PulseCrawl" \
4
+ org.opencontainers.image.description="PulseCrawl local workspace scheduler" \
5
+ org.opencontainers.image.licenses="AGPL-3.0-only"
6
+
7
+ ENV PYTHONDONTWRITEBYTECODE=1 \
8
+ PYTHONUNBUFFERED=1 \
9
+ PIP_NO_CACHE_DIR=1
10
+
11
+ WORKDIR /opt/pulsecrawl
12
+ COPY . /opt/pulsecrawl/package
13
+ RUN python -m pip install --no-cache-dir --upgrade "pip==26.2.1" \
14
+ && python -m pip install --no-cache-dir \
15
+ --constraint /opt/pulsecrawl/package/requirements-workspace.txt \
16
+ '/opt/pulsecrawl/package[workspace]'
17
+
18
+ RUN useradd --create-home --uid 10001 pulsecrawl \
19
+ && mkdir -p /workspace \
20
+ && chown pulsecrawl:pulsecrawl /workspace
21
+ USER pulsecrawl
22
+
23
+ ENTRYPOINT ["pulsecrawl"]
24
+ CMD ["--workspace", "/workspace", "schedule", "start"]
@@ -0,0 +1,15 @@
1
+ # Only build inputs enter the container context: no Git data, credentials,
2
+ # local environments, test outputs, or previously built distributions.
3
+ **
4
+ !pyproject.toml
5
+ !_build_backend.py
6
+ !PYPI_README.md
7
+ !README.md
8
+ !LICENSE
9
+ !MANIFEST.in
10
+ !requirements-workspace.txt
11
+ !src/
12
+ !src/pulsecrawl/
13
+ !src/pulsecrawl/**
14
+ **/__pycache__/
15
+ **/*.py[cod]