mdfetch 0.9.2__tar.gz → 0.9.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. mdfetch-0.9.4/.github/dependabot.yml +21 -0
  2. {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/workflows/ci.yml +4 -4
  3. {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/workflows/integration.yml +3 -3
  4. {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/workflows/publish.yml +7 -7
  5. {mdfetch-0.9.2 → mdfetch-0.9.4}/PKG-INFO +12 -2
  6. {mdfetch-0.9.2 → mdfetch-0.9.4}/README.md +10 -0
  7. {mdfetch-0.9.2 → mdfetch-0.9.4}/pyproject.toml +1 -1
  8. mdfetch-0.9.4/src/mdfetch/fallback.py +53 -0
  9. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_fallback.py +27 -3
  10. {mdfetch-0.9.2 → mdfetch-0.9.4}/uv.lock +28 -9
  11. mdfetch-0.9.2/.vscode/settings.json +0 -10
  12. mdfetch-0.9.2/src/mdfetch/fallback.py +0 -64
  13. mdfetch-0.9.2/tests/integration_log.txt +0 -1
  14. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-analyze/SKILL.md +0 -0
  15. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-checklist/SKILL.md +0 -0
  16. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-clarify/SKILL.md +0 -0
  17. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-constitution/SKILL.md +0 -0
  18. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-converge/SKILL.md +0 -0
  19. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-implement/SKILL.md +0 -0
  20. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-plan/SKILL.md +0 -0
  21. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-specify/SKILL.md +0 -0
  22. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-tasks/SKILL.md +0 -0
  23. {mdfetch-0.9.2 → mdfetch-0.9.4}/.agents/skills/speckit-taskstoissues/SKILL.md +0 -0
  24. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-analyze/SKILL.md +0 -0
  25. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-archive-run/SKILL.md +0 -0
  26. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-checklist/SKILL.md +0 -0
  27. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-clarify/SKILL.md +0 -0
  28. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-constitution/SKILL.md +0 -0
  29. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-converge/SKILL.md +0 -0
  30. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
  31. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
  32. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
  33. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
  34. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
  35. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-implement/SKILL.md +0 -0
  36. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-plan/SKILL.md +0 -0
  37. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-reconcile-run/SKILL.md +0 -0
  38. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-specify/SKILL.md +0 -0
  39. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-tasks/SKILL.md +0 -0
  40. {mdfetch-0.9.2 → mdfetch-0.9.4}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
  41. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.analyze.toml +0 -0
  42. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.checklist.toml +0 -0
  43. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.clarify.toml +0 -0
  44. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.constitution.toml +0 -0
  45. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.implement.toml +0 -0
  46. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.plan.toml +0 -0
  47. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.specify.toml +0 -0
  48. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.tasks.toml +0 -0
  49. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gemini/commands/speckit.taskstoissues.toml +0 -0
  50. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gitattributes +0 -0
  51. {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/copilot-instructions.md +0 -0
  52. {mdfetch-0.9.2 → mdfetch-0.9.4}/.github/scripts/generate_resources.py +0 -0
  53. {mdfetch-0.9.2 → mdfetch-0.9.4}/.gitignore +0 -0
  54. {mdfetch-0.9.2 → mdfetch-0.9.4}/.python-version +0 -0
  55. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/.registry +0 -0
  56. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/LICENSE +0 -0
  57. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/README.md +0 -0
  58. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/commands/archive.md +0 -0
  59. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/archive/extension.yml +0 -0
  60. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/README.md +0 -0
  61. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
  62. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
  63. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
  64. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
  65. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
  66. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/config-template.yml +0 -0
  67. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/extension.yml +0 -0
  68. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/git-config.yml +0 -0
  69. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
  70. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
  71. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
  72. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  73. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  74. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
  75. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  76. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  77. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/LICENSE +0 -0
  78. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/README.md +0 -0
  79. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/commands/reconcile.md +0 -0
  80. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions/reconcile/extension.yml +0 -0
  81. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/extensions.yml +0 -0
  82. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/feature.json +0 -0
  83. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/init-options.json +0 -0
  84. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integration.json +0 -0
  85. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/agy.manifest.json +0 -0
  86. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/claude.manifest.json +0 -0
  87. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/gemini.manifest.json +0 -0
  88. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/integrations/speckit.manifest.json +0 -0
  89. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/changelog.md +0 -0
  90. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/constitution.md +0 -0
  91. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/plan.md +0 -0
  92. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/memory/spec.md +0 -0
  93. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  94. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/common.sh +0 -0
  95. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/create-new-feature.sh +0 -0
  96. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/setup-plan.sh +0 -0
  97. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/scripts/bash/setup-tasks.sh +0 -0
  98. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/checklist-template.md +0 -0
  99. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/constitution-template.md +0 -0
  100. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/plan-template.md +0 -0
  101. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/spec-template.md +0 -0
  102. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/templates/tasks-template.md +0 -0
  103. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/workflows/speckit/workflow.yml +0 -0
  104. {mdfetch-0.9.2 → mdfetch-0.9.4}/.specify/workflows/workflow-registry.json +0 -0
  105. {mdfetch-0.9.2 → mdfetch-0.9.4}/CLAUDE.md +0 -0
  106. {mdfetch-0.9.2 → mdfetch-0.9.4}/GEMINI.md +0 -0
  107. {mdfetch-0.9.2 → mdfetch-0.9.4}/LICENSE +0 -0
  108. {mdfetch-0.9.2 → mdfetch-0.9.4}/Makefile +0 -0
  109. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/checklists/requirements.md +0 -0
  110. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/contracts/api.md +0 -0
  111. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/data-model.md +0 -0
  112. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/plan.md +0 -0
  113. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/quickstart.md +0 -0
  114. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/research.md +0 -0
  115. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/spec.md +0 -0
  116. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/001-mdfetch-medium-extractor/tasks.md +0 -0
  117. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/checklists/requirements.md +0 -0
  118. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/contracts/public-api.md +0 -0
  119. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/data-model.md +0 -0
  120. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/plan.md +0 -0
  121. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/quickstart.md +0 -0
  122. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/research.md +0 -0
  123. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/spec.md +0 -0
  124. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/002-devto-provider/tasks.md +0 -0
  125. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/checklists/requirements.md +0 -0
  126. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/contracts/extract-api.md +0 -0
  127. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/plan.md +0 -0
  128. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/research.md +0 -0
  129. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/spec.md +0 -0
  130. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/003-medium-freedium-fallback/tasks.md +0 -0
  131. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/checklists/requirements.md +0 -0
  132. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/plan.md +0 -0
  133. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/research.md +0 -0
  134. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/spec.md +0 -0
  135. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/004-remove-backoff/tasks.md +0 -0
  136. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/checklists/requirements.md +0 -0
  137. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/contracts/extractor-api.md +0 -0
  138. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/data-model.md +0 -0
  139. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/plan.md +0 -0
  140. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/quickstart.md +0 -0
  141. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/research.md +0 -0
  142. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/spec.md +0 -0
  143. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/005-substack-provider/tasks.md +0 -0
  144. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/checklists/requirements.md +0 -0
  145. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/contracts/public-api.md +0 -0
  146. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/data-model.md +0 -0
  147. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/plan.md +0 -0
  148. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/quickstart.md +0 -0
  149. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/research.md +0 -0
  150. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/spec.md +0 -0
  151. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/006-thenewstack-provider/tasks.md +0 -0
  152. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/checklists/requirements.md +0 -0
  153. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/contracts/public-api.md +0 -0
  154. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/data-model.md +0 -0
  155. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/plan.md +0 -0
  156. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/quickstart.md +0 -0
  157. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/research.md +0 -0
  158. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/spec.md +0 -0
  159. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/007-dzone-provider/tasks.md +0 -0
  160. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/checklists/requirements.md +0 -0
  161. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/contracts/cli.md +0 -0
  162. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/data-model.md +0 -0
  163. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/plan.md +0 -0
  164. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/quickstart.md +0 -0
  165. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/research.md +0 -0
  166. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/spec.md +0 -0
  167. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/008-mdfetch-cli/tasks.md +0 -0
  168. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/checklists/requirements.md +0 -0
  169. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/contracts/formula.md +0 -0
  170. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/contracts/tap-update-job.md +0 -0
  171. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/data-model.md +0 -0
  172. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/plan.md +0 -0
  173. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/quickstart.md +0 -0
  174. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/research.md +0 -0
  175. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/spec.md +0 -0
  176. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/009-homebrew-tap-formula/tasks.md +0 -0
  177. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/checklists/requirements.md +0 -0
  178. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/contracts/extractor-contract.md +0 -0
  179. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/data-model.md +0 -0
  180. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/plan.md +0 -0
  181. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/quickstart.md +0 -0
  182. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/research.md +0 -0
  183. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/spec.md +0 -0
  184. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/010-boomi-blog-provider/tasks.md +0 -0
  185. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/checklists/requirements.md +0 -0
  186. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/contracts/extractor-contract.md +0 -0
  187. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/data-model.md +0 -0
  188. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/plan.md +0 -0
  189. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/quickstart.md +0 -0
  190. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/research.md +0 -0
  191. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/spec.md +0 -0
  192. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/011-konghq-blog-provider/tasks.md +0 -0
  193. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/checklists/requirements.md +0 -0
  194. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/contracts/cli.md +0 -0
  195. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/data-model.md +0 -0
  196. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/plan.md +0 -0
  197. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/quickstart.md +0 -0
  198. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/research.md +0 -0
  199. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/spec.md +0 -0
  200. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/012-list-platforms/tasks.md +0 -0
  201. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/checklists/requirements.md +0 -0
  202. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/contracts/api.md +0 -0
  203. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/contracts/cli.md +0 -0
  204. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/data-model.md +0 -0
  205. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/plan.md +0 -0
  206. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/quickstart.md +0 -0
  207. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/research.md +0 -0
  208. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/spec.md +0 -0
  209. {mdfetch-0.9.2 → mdfetch-0.9.4}/specs/013-tavily-fallback/tasks.md +0 -0
  210. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/__init__.py +0 -0
  211. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/base.py +0 -0
  212. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/cli.py +0 -0
  213. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/exceptions.py +0 -0
  214. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/__init__.py +0 -0
  215. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/boomi.py +0 -0
  216. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/devto.py +0 -0
  217. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/dzone.py +0 -0
  218. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/kong.py +0 -0
  219. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/medium.py +0 -0
  220. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/substack.py +0 -0
  221. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/providers/thenewstack.py +0 -0
  222. {mdfetch-0.9.2 → mdfetch-0.9.4}/src/mdfetch/router.py +0 -0
  223. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/__init__.py +0 -0
  224. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/conftest.py +0 -0
  225. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/__init__.py +0 -0
  226. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/conftest.py +0 -0
  227. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/architecting-the-asynchronous-agent.md +0 -0
  228. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/boomi-data-consistency-saas-on-prem.md +0 -0
  229. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/boomi-gartner-magic-quadrant-ipaas-2026.md +0 -0
  230. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/boomi-real-time-vs-batch-data-integration.md +0 -0
  231. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/devto-integration-digest-december-2025.md +0 -0
  232. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/devto-integration-digest-july-2025.md +0 -0
  233. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/devto-integration-digest-march-2026.md +0 -0
  234. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/dzone-image-classification-pipeline-camel-djl.md +0 -0
  235. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/dzone-integration-patterns-fail-production.md +0 -0
  236. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/dzone-kiro-feature-to-requirements-design-tasks.md +0 -0
  237. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/from-drift-to-parity.md +0 -0
  238. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/integration-digest-december-2025.md +0 -0
  239. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/kong-ai-gateway-vs-litellm.md +0 -0
  240. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/kong-gateway-3-14.md +0 -0
  241. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/kong-insomnia-12-6.md +0 -0
  242. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/substack-api-trends-2025.md +0 -0
  243. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/substack-kafka-topic-types.md +0 -0
  244. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-api-mcp-agent.md +0 -0
  245. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-async-apis.md +0 -0
  246. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-developer-portal-api.md +0 -0
  247. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-json-schema-ai.md +0 -0
  248. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/snapshots/thenewstack-mcp-api-governance.md +0 -0
  249. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_boomi_integration.py +0 -0
  250. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_cli_integration.py +0 -0
  251. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_devto_integration.py +0 -0
  252. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_dzone_integration.py +0 -0
  253. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_fallback_integration.py +0 -0
  254. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_kong_integration.py +0 -0
  255. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_medium_integration.py +0 -0
  256. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_substack_integration.py +0 -0
  257. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/integration/test_thenewstack_integration.py +0 -0
  258. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/__init__.py +0 -0
  259. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_boomi_extractor.py +0 -0
  260. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_cli.py +0 -0
  261. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_devto_extractor.py +0 -0
  262. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_dzone_extractor.py +0 -0
  263. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_fetch_errors.py +0 -0
  264. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_kong_extractor.py +0 -0
  265. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_medium_extractor.py +0 -0
  266. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_router.py +0 -0
  267. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_silent.py +0 -0
  268. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_substack_extractor.py +0 -0
  269. {mdfetch-0.9.2 → mdfetch-0.9.4}/tests/unit/test_thenewstack_extractor.py +0 -0
@@ -0,0 +1,21 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ open-pull-requests-limit: 5
8
+ groups:
9
+ python-deps:
10
+ patterns:
11
+ - "*"
12
+
13
+ - package-ecosystem: github-actions
14
+ directory: /
15
+ schedule:
16
+ interval: weekly
17
+ open-pull-requests-limit: 5
18
+ groups:
19
+ actions:
20
+ patterns:
21
+ - "*"
@@ -12,9 +12,9 @@ jobs:
12
12
  lint:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16
16
  - name: Install uv
17
- uses: astral-sh/setup-uv@v4
17
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
18
18
  with:
19
19
  enable-cache: true
20
20
  cache-dependency-glob: "uv.lock"
@@ -36,9 +36,9 @@ jobs:
36
36
  matrix:
37
37
  python-version: ["3.12", "3.13", "3.14"]
38
38
  steps:
39
- - uses: actions/checkout@v4
39
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
40
40
  - name: Install uv
41
- uses: astral-sh/setup-uv@v4
41
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
42
42
  with:
43
43
  enable-cache: true
44
44
  cache-dependency-glob: "uv.lock"
@@ -14,10 +14,10 @@ jobs:
14
14
  runs-on: [self-hosted, Linux, ARM64]
15
15
  timeout-minutes: 30
16
16
  steps:
17
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
18
 
19
19
  - name: Install uv
20
- uses: astral-sh/setup-uv@v4
20
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
21
21
  with:
22
22
  enable-cache: true
23
23
  cache-dependency-glob: "uv.lock"
@@ -34,7 +34,7 @@ jobs:
34
34
 
35
35
  - name: Create issue on failure
36
36
  if: failure() && steps.integration.outcome == 'failure'
37
- uses: actions/github-script@v7
37
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
38
38
  with:
39
39
  script: |
40
40
  const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
@@ -13,9 +13,9 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  if: github.event.release.target_commitish == 'main'
15
15
  steps:
16
- - uses: actions/checkout@v4
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
17
  - name: Install uv
18
- uses: astral-sh/setup-uv@v4
18
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
19
19
  - name: Set up Python
20
20
  run: uv python pin 3.12
21
21
  - name: Validate version
@@ -29,7 +29,7 @@ jobs:
29
29
  - name: Build package
30
30
  run: make build
31
31
  - name: Store the distribution packages
32
- uses: actions/upload-artifact@v4
32
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
33
33
  with:
34
34
  name: python-package-distributions
35
35
  path: dist/
@@ -45,12 +45,12 @@ jobs:
45
45
  id-token: write
46
46
  steps:
47
47
  - name: Download all the dists
48
- uses: actions/download-artifact@v4
48
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
49
49
  with:
50
50
  name: python-package-distributions
51
51
  path: dist/
52
52
  - name: Publish package distributions to PyPI
53
- uses: pypa/gh-action-pypi-publish@release/v1
53
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
54
54
  with:
55
55
  packages-dir: dist/
56
56
 
@@ -65,10 +65,10 @@ jobs:
65
65
  contents: read
66
66
 
67
67
  steps:
68
- - uses: actions/checkout@v4
68
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
69
69
 
70
70
  - name: Install uv
71
- uses: astral-sh/setup-uv@v4
71
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
72
72
 
73
73
  - name: Set up Python
74
74
  run: uv python pin 3.12
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: mdfetch
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Extract article content from web platforms and return it as clean Markdown.
5
5
  Project-URL: Homepage, https://github.com/stn1slv/md-fetch
6
6
  Project-URL: Source, https://github.com/stn1slv/md-fetch
@@ -63,6 +63,10 @@ md-fetch https://medium.com/example/article
63
63
  # Fetch and save Markdown to a file
64
64
  md-fetch https://dev.to/example/article --output article.md
65
65
 
66
+ # Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY)
67
+ export TAVILY_API_KEY="tvly-..."
68
+ md-fetch https://example.com/unsupported-blog --tavily-fallback
69
+
66
70
  # List all supported platforms (no URL or network needed)
67
71
  md-fetch --list-platforms
68
72
  ```
@@ -80,6 +84,9 @@ markdown = extract("https://thenewstack.io/article-slug")
80
84
  markdown = extract("https://dzone.com/articles/article-slug")
81
85
  markdown = extract("https://boomi.com/blog/article-slug")
82
86
  markdown = extract("https://konghq.com/blog/category/article-slug")
87
+
88
+ # Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY env var)
89
+ markdown = extract("https://example.com/unsupported-blog", tavily_fallback=True)
83
90
  print(markdown)
84
91
  ```
85
92
 
@@ -94,6 +101,7 @@ from mdfetch import (
94
101
  FetchError,
95
102
  HTTPStatusError,
96
103
  EmptyContentError,
104
+ MissingAPIKeyError,
97
105
  )
98
106
 
99
107
  url = "https://medium.com/some-publication/article-slug-abc123"
@@ -112,6 +120,8 @@ except FetchError as e:
112
120
  print(f"Network error: {e.message}")
113
121
  except EmptyContentError as e:
114
122
  print(f"No content: {e.message}")
123
+ except MissingAPIKeyError as e:
124
+ print(f"Missing API Key: {e.message}")
115
125
  ```
116
126
 
117
127
  ## Supported platforms
@@ -29,6 +29,10 @@ md-fetch https://medium.com/example/article
29
29
  # Fetch and save Markdown to a file
30
30
  md-fetch https://dev.to/example/article --output article.md
31
31
 
32
+ # Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY)
33
+ export TAVILY_API_KEY="tvly-..."
34
+ md-fetch https://example.com/unsupported-blog --tavily-fallback
35
+
32
36
  # List all supported platforms (no URL or network needed)
33
37
  md-fetch --list-platforms
34
38
  ```
@@ -46,6 +50,9 @@ markdown = extract("https://thenewstack.io/article-slug")
46
50
  markdown = extract("https://dzone.com/articles/article-slug")
47
51
  markdown = extract("https://boomi.com/blog/article-slug")
48
52
  markdown = extract("https://konghq.com/blog/category/article-slug")
53
+
54
+ # Fallback to Tavily API if the primary extractor fails (requires TAVILY_API_KEY env var)
55
+ markdown = extract("https://example.com/unsupported-blog", tavily_fallback=True)
49
56
  print(markdown)
50
57
  ```
51
58
 
@@ -60,6 +67,7 @@ from mdfetch import (
60
67
  FetchError,
61
68
  HTTPStatusError,
62
69
  EmptyContentError,
70
+ MissingAPIKeyError,
63
71
  )
64
72
 
65
73
  url = "https://medium.com/some-publication/article-slug-abc123"
@@ -78,6 +86,8 @@ except FetchError as e:
78
86
  print(f"Network error: {e.message}")
79
87
  except EmptyContentError as e:
80
88
  print(f"No content: {e.message}")
89
+ except MissingAPIKeyError as e:
90
+ print(f"Missing API Key: {e.message}")
81
91
  ```
82
92
 
83
93
  ## Supported platforms
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mdfetch"
7
- version = "0.9.2"
7
+ version = "0.9.4"
8
8
  description = "Extract article content from web platforms and return it as clean Markdown."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1,53 @@
1
+ """Fallback extraction mechanisms for mdfetch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from mdfetch.exceptions import EmptyContentError, FetchError, MissingAPIKeyError
8
+
9
+
10
+ def _extract(client: Any, url: str, depth: str, timeout: float) -> str:
11
+ response = client.extract(urls=[url], extract_depth=depth, timeout=timeout)
12
+ results = response.get("results", [])
13
+ if not results:
14
+ raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
15
+
16
+ raw_content = results[0].get("raw_content")
17
+ if not raw_content:
18
+ raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
19
+
20
+ return str(raw_content)
21
+
22
+
23
+ def tavily_extract(url: str, timeout: float = 30.0) -> str:
24
+ """Extract content using the Tavily API fallback.
25
+
26
+ Tries the cheaper "basic" depth first. If it fails or returns nothing, retries
27
+ once with "advanced". Empty content from the retry raises EmptyContentError;
28
+ any other failure raises FetchError.
29
+ """
30
+ try:
31
+ from tavily import TavilyClient # type: ignore[import-untyped]
32
+ except ImportError as e:
33
+ raise MissingAPIKeyError(
34
+ "tavily-python package is not installed. Please install it "
35
+ f"to use the fallback feature. {e}"
36
+ ) from e
37
+
38
+ try:
39
+ client = TavilyClient()
40
+ except Exception as e:
41
+ raise MissingAPIKeyError(str(e)) from e
42
+
43
+ try:
44
+ return _extract(client, url, "basic", timeout)
45
+ except Exception:
46
+ pass # any basic-depth failure is retried with advanced below
47
+
48
+ try:
49
+ return _extract(client, url, "advanced", timeout)
50
+ except EmptyContentError:
51
+ raise
52
+ except Exception as e:
53
+ raise FetchError(f"Tavily fallback failed: {e}", url=url) from e
@@ -7,7 +7,7 @@ from unittest.mock import MagicMock, patch
7
7
  import pytest
8
8
 
9
9
  from mdfetch import extract
10
- from mdfetch.exceptions import FetchError, MissingAPIKeyError
10
+ from mdfetch.exceptions import EmptyContentError, FetchError, MissingAPIKeyError
11
11
  from mdfetch.fallback import tavily_extract
12
12
 
13
13
 
@@ -21,7 +21,7 @@ class TestTavilyExtract:
21
21
 
22
22
  assert result == "# Hello"
23
23
  mock_instance.extract.assert_called_once_with(
24
- urls=["https://example.com"], extract_depth="basic"
24
+ urls=["https://example.com"], extract_depth="basic", timeout=30.0
25
25
  )
26
26
 
27
27
  @patch("tavily.TavilyClient")
@@ -38,7 +38,7 @@ class TestTavilyExtract:
38
38
  assert result == "# Advanced"
39
39
  assert mock_instance.extract.call_count == 2
40
40
  mock_instance.extract.assert_any_call(
41
- urls=["https://example.com"], extract_depth="advanced"
41
+ urls=["https://example.com"], extract_depth="advanced", timeout=30.0
42
42
  )
43
43
 
44
44
  @patch("tavily.TavilyClient")
@@ -63,6 +63,30 @@ class TestTavilyExtract:
63
63
  with pytest.raises(FetchError):
64
64
  tavily_extract("https://example.com")
65
65
 
66
+ @patch("tavily.TavilyClient")
67
+ def test_empty_advanced_after_basic_error_raises_empty_content(
68
+ self, mock_tavily_client: MagicMock
69
+ ) -> None:
70
+ mock_instance = mock_tavily_client.return_value
71
+ mock_instance.extract.side_effect = [
72
+ Exception("API Error"),
73
+ {"results": []},
74
+ ]
75
+
76
+ with pytest.raises(EmptyContentError):
77
+ tavily_extract("https://example.com")
78
+
79
+ @patch("tavily.TavilyClient")
80
+ def test_timeout_is_passed_to_tavily(self, mock_tavily_client: MagicMock) -> None:
81
+ mock_instance = mock_tavily_client.return_value
82
+ mock_instance.extract.return_value = {"results": [{"raw_content": "# Hello"}]}
83
+
84
+ tavily_extract("https://example.com", timeout=5.0)
85
+
86
+ mock_instance.extract.assert_called_once_with(
87
+ urls=["https://example.com"], extract_depth="basic", timeout=5.0
88
+ )
89
+
66
90
 
67
91
  class TestExtractRouting:
68
92
  @patch("mdfetch.tavily_extract")
@@ -8,15 +8,33 @@ resolution-markers = [
8
8
 
9
9
  [[package]]
10
10
  name = "anyio"
11
- version = "4.13.0"
11
+ version = "4.14.2"
12
12
  source = { registry = "https://pypi.org/simple" }
13
+ resolution-markers = [
14
+ "python_full_version < '3.15'",
15
+ ]
16
+ dependencies = [
17
+ { name = "idna" },
18
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or python_full_version >= '3.15'" },
19
+ ]
20
+ sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
21
+ wheels = [
22
+ { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
23
+ ]
24
+
25
+ [[package]]
26
+ name = "anyio"
27
+ version = "4.15.1"
28
+ source = { registry = "https://pypi.org/simple" }
29
+ resolution-markers = [
30
+ "python_full_version >= '3.15'",
31
+ ]
13
32
  dependencies = [
14
33
  { name = "idna" },
15
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
16
34
  ]
17
- sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
35
+ sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" }
18
36
  wheels = [
19
- { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
37
+ { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
20
38
  ]
21
39
 
22
40
  [[package]]
@@ -188,7 +206,8 @@ name = "httpx"
188
206
  version = "0.28.1"
189
207
  source = { registry = "https://pypi.org/simple" }
190
208
  dependencies = [
191
- { name = "anyio" },
209
+ { name = "anyio", version = "4.14.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.15'" },
210
+ { name = "anyio", version = "4.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.15'" },
192
211
  { name = "certifi" },
193
212
  { name = "httpcore" },
194
213
  { name = "idna" },
@@ -371,7 +390,7 @@ wheels = [
371
390
 
372
391
  [[package]]
373
392
  name = "mdfetch"
374
- version = "0.9.2"
393
+ version = "0.9.4"
375
394
  source = { editable = "." }
376
395
  dependencies = [
377
396
  { name = "beautifulsoup4" },
@@ -661,11 +680,11 @@ wheels = [
661
680
 
662
681
  [[package]]
663
682
  name = "soupsieve"
664
- version = "2.8.4"
683
+ version = "2.10"
665
684
  source = { registry = "https://pypi.org/simple" }
666
- sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" }
685
+ sdist = { url = "https://files.pythonhosted.org/packages/71/c3/1b817965ac12dc002d7c9cd7dfffdd7d4fbf9b45763ef2ffe7b86ee94670/soupsieve-2.10.tar.gz", hash = "sha256:49e9380d7d2905463583bafe285e818c7366a9ed7b3aee221c1ac79c905d8bc0", size = 124337, upload-time = "2026-09-24T02:36:35.048Z" }
667
686
  wheels = [
668
- { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" },
687
+ { url = "https://files.pythonhosted.org/packages/66/87/5ed59e1d0290564e2027ed066c52b059892c4637741337fc0967183c8d4d/soupsieve-2.10-py3-none-any.whl", hash = "sha256:8596eb8967d744174820280fa62b4542a2e955bfaccca73ed8a13c6eb8e9b502", size = 38019, upload-time = "2026-09-24T02:36:33.709Z" },
669
688
  ]
670
689
 
671
690
  [[package]]
@@ -1,10 +0,0 @@
1
- {
2
- "files.exclude": {
3
- "**/.git": true,
4
- "**/.svn": true,
5
- "**/.hg": true,
6
- "**/.DS_Store": true,
7
- "**/Thumbs.db": true,
8
- ".mule": true
9
- }
10
- }
@@ -1,64 +0,0 @@
1
- """Fallback extraction mechanisms for mdfetch."""
2
-
3
- from __future__ import annotations
4
-
5
- from mdfetch.exceptions import EmptyContentError, FetchError, MissingAPIKeyError
6
-
7
-
8
- def tavily_extract(url: str, timeout: float = 30.0) -> str:
9
- """Extract content using the Tavily API fallback."""
10
- try:
11
- from tavily import TavilyClient # type: ignore[import-untyped]
12
- except ImportError as e:
13
- raise MissingAPIKeyError(
14
- "tavily-python package is not installed. Please install it "
15
- f"to use the fallback feature. {e}"
16
- ) from e
17
-
18
- try:
19
- client = TavilyClient()
20
- except Exception as e:
21
- raise MissingAPIKeyError(str(e)) from e
22
-
23
- try:
24
- response = client.extract(urls=[url], extract_depth="basic")
25
- results = response.get("results", [])
26
- if not results:
27
- raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
28
-
29
- raw_content = results[0].get("raw_content")
30
- if not raw_content:
31
- raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
32
-
33
- return str(raw_content)
34
- except EmptyContentError:
35
- try:
36
- response = client.extract(urls=[url], extract_depth="advanced")
37
- results = response.get("results", [])
38
- if not results:
39
- raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
40
-
41
- raw_content = results[0].get("raw_content")
42
- if not raw_content:
43
- raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
44
-
45
- return str(raw_content)
46
- except Exception as e:
47
- if isinstance(e, EmptyContentError):
48
- raise
49
- raise FetchError(f"Tavily fallback failed: {e}", url=url) from e
50
- except Exception:
51
- # If basic threw an API error, fallback to advanced as well
52
- try:
53
- response = client.extract(urls=[url], extract_depth="advanced")
54
- results = response.get("results", [])
55
- if not results:
56
- raise EmptyContentError(f"Tavily returned no results for {url}", url=url)
57
-
58
- raw_content = results[0].get("raw_content")
59
- if not raw_content:
60
- raise EmptyContentError(f"Tavily returned empty content for {url}", url=url)
61
-
62
- return str(raw_content)
63
- except Exception as e:
64
- raise FetchError(f"Tavily fallback failed: {e}", url=url) from e
@@ -1 +0,0 @@
1
- [{"path":".github","blob_href":"https://github.com/stn1slv/md-fetch/blob/9104266a16d8dbd63c1bb011df63a137f53987b5/.github","start_line":2,"start_column":null,"end_line":2,"end_column":null,"annotation_level":"warning","title":"","message":"Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/github-script@v7, astral-sh/setup-uv@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/","raw_details":""},{"path":".github","blob_href":"https://github.com/stn1slv/md-fetch/blob/9104266a16d8dbd63c1bb011df63a137f53987b5/.github","start_line":133,"start_column":null,"end_line":133,"end_column":null,"annotation_level":"failure","title":"","message":"Process completed with exit code 2.","raw_details":""},{"path":".github","blob_href":"https://github.com/stn1slv/md-fetch/blob/9104266a16d8dbd63c1bb011df63a137f53987b5/.github","start_line":22,"start_column":null,"end_line":22,"end_column":null,"annotation_level":"warning","title":"","message":"Failed to restore: Cache service responded with 400","raw_details":""}]
File without changes
File without changes
File without changes