bluera-knowledge 0.9.21
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.
- package/.claude/commands/commit.md +37 -0
- package/.claude/hooks/post-edit-check.sh +41 -0
- package/.claude/settings.local.json.example +40 -0
- package/.claude/skills/atomic-commits/SKILL.md +53 -0
- package/.claude-plugin/plugin.json +13 -0
- package/.editorconfig +15 -0
- package/.github/workflows/auto-release.yml +59 -0
- package/.github/workflows/ci.yml +142 -0
- package/.github/workflows/release.yml +66 -0
- package/.github/workflows/update-marketplace.yml +96 -0
- package/.husky/pre-commit +47 -0
- package/.husky/pre-push +29 -0
- package/.versionrc.json +28 -0
- package/CHANGELOG.md +410 -0
- package/CLAUDE.md +109 -0
- package/LICENSE +21 -0
- package/NOTICE +47 -0
- package/README.md +1546 -0
- package/SECURITY.md +65 -0
- package/bun.lock +1758 -0
- package/commands/add-folder.md +48 -0
- package/commands/add-repo.md +50 -0
- package/commands/cancel.md +63 -0
- package/commands/check-status.md +78 -0
- package/commands/crawl.md +51 -0
- package/commands/index.md +48 -0
- package/commands/remove-store.md +52 -0
- package/commands/search.md +79 -0
- package/commands/search.sh +63 -0
- package/commands/stores.md +54 -0
- package/commands/suggest.md +82 -0
- package/dist/chunk-5QMHZUC4.js +3617 -0
- package/dist/chunk-5QMHZUC4.js.map +1 -0
- package/dist/chunk-BICFAWMN.js +656 -0
- package/dist/chunk-BICFAWMN.js.map +1 -0
- package/dist/chunk-J7J6LXOJ.js +958 -0
- package/dist/chunk-J7J6LXOJ.js.map +1 -0
- package/dist/chunk-L2YVNC63.js +59 -0
- package/dist/chunk-L2YVNC63.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1429 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +15 -0
- package/dist/mcp/server.js +11 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/watch.service-YAIKKDCF.js +7 -0
- package/dist/watch.service-YAIKKDCF.js.map +1 -0
- package/dist/workers/background-worker-cli.d.ts +1 -0
- package/dist/workers/background-worker-cli.js +310 -0
- package/dist/workers/background-worker-cli.js.map +1 -0
- package/docs/plans/2024-12-17-ai-search-quality-implementation.md +752 -0
- package/docs/plans/2024-12-17-ai-search-quality-testing-design.md +201 -0
- package/docs/plans/2025-12-16-bluera-knowledge-cli.md +2951 -0
- package/docs/plans/2025-12-16-phase2-features.md +1518 -0
- package/docs/plans/2025-12-17-hil-implementation.md +926 -0
- package/docs/plans/2025-12-17-hil-quality-testing.md +224 -0
- package/docs/plans/2025-12-17-search-quality-phase1-implementation.md +1416 -0
- package/docs/plans/2025-12-17-search-quality-testing-v2-design.md +212 -0
- package/docs/plans/2025-12-28-ai-agent-optimization.md +1630 -0
- package/eslint-rules/require-skip-comment.js +81 -0
- package/eslint.config.js +61 -0
- package/hooks/check-dependencies.sh +110 -0
- package/hooks/format-search-results.py +132 -0
- package/hooks/hooks.json +27 -0
- package/hooks/job-status-hook.sh +51 -0
- package/knip.json +43 -0
- package/mcp.plugin.json +12 -0
- package/package.json +103 -0
- package/python/crawl_worker.py +275 -0
- package/python/requirements.txt +2 -0
- package/scripts/readme-version-updater.cjs +18 -0
- package/skills/advanced-workflows/SKILL.md +273 -0
- package/skills/atomic-commits/SKILL.md +77 -0
- package/skills/knowledge-search/SKILL.md +54 -0
- package/skills/search-optimization/SKILL.md +396 -0
- package/skills/store-lifecycle/SKILL.md +470 -0
- package/skills/when-to-query/SKILL.md +66 -0
- package/src/analysis/ast-parser.test.ts +423 -0
- package/src/analysis/ast-parser.ts +192 -0
- package/src/analysis/code-graph.test.ts +698 -0
- package/src/analysis/code-graph.ts +245 -0
- package/src/analysis/dependency-usage-analyzer.test.ts +799 -0
- package/src/analysis/dependency-usage-analyzer.ts +405 -0
- package/src/analysis/go-ast-parser.test.ts +531 -0
- package/src/analysis/go-ast-parser.ts +478 -0
- package/src/analysis/parser-factory.test.ts +132 -0
- package/src/analysis/parser-factory.ts +44 -0
- package/src/analysis/python-ast-parser.test.ts +210 -0
- package/src/analysis/python-ast-parser.ts +34 -0
- package/src/analysis/repo-url-resolver.test.ts +533 -0
- package/src/analysis/repo-url-resolver.ts +233 -0
- package/src/analysis/rust-ast-parser.test.ts +568 -0
- package/src/analysis/rust-ast-parser.ts +477 -0
- package/src/analysis/tree-sitter-parser.test.ts +297 -0
- package/src/analysis/tree-sitter-parser.ts +223 -0
- package/src/cli/commands/crawl.test.ts +942 -0
- package/src/cli/commands/crawl.ts +141 -0
- package/src/cli/commands/index-cmd.test.ts +722 -0
- package/src/cli/commands/index-cmd.ts +105 -0
- package/src/cli/commands/mcp.test.ts +218 -0
- package/src/cli/commands/mcp.ts +18 -0
- package/src/cli/commands/plugin-api.test.ts +313 -0
- package/src/cli/commands/plugin-api.ts +45 -0
- package/src/cli/commands/search.test.ts +911 -0
- package/src/cli/commands/search.ts +113 -0
- package/src/cli/commands/serve.test.ts +329 -0
- package/src/cli/commands/serve.ts +28 -0
- package/src/cli/commands/setup.test.ts +820 -0
- package/src/cli/commands/setup.ts +153 -0
- package/src/cli/commands/store.test.ts +1003 -0
- package/src/cli/commands/store.ts +167 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/program.ts +59 -0
- package/src/crawl/article-converter.test.ts +604 -0
- package/src/crawl/article-converter.ts +98 -0
- package/src/crawl/bridge.test.ts +674 -0
- package/src/crawl/bridge.ts +236 -0
- package/src/crawl/claude-client.test.ts +663 -0
- package/src/crawl/claude-client.ts +234 -0
- package/src/crawl/intelligent-crawler.test.ts +931 -0
- package/src/crawl/intelligent-crawler.ts +428 -0
- package/src/crawl/markdown-utils.test.ts +703 -0
- package/src/crawl/markdown-utils.ts +228 -0
- package/src/crawl/schemas.ts +114 -0
- package/src/db/embeddings.test.ts +63 -0
- package/src/db/embeddings.ts +69 -0
- package/src/db/index.ts +2 -0
- package/src/db/lance.test.ts +390 -0
- package/src/db/lance.ts +164 -0
- package/src/defaults/repos.ts +67 -0
- package/src/index.ts +107 -0
- package/src/mcp/cache.test.ts +202 -0
- package/src/mcp/cache.ts +103 -0
- package/src/mcp/commands/index.ts +20 -0
- package/src/mcp/commands/job.commands.ts +54 -0
- package/src/mcp/commands/meta.commands.ts +54 -0
- package/src/mcp/commands/registry.ts +183 -0
- package/src/mcp/commands/store.commands.ts +75 -0
- package/src/mcp/handlers/execute.handler.test.ts +179 -0
- package/src/mcp/handlers/execute.handler.ts +24 -0
- package/src/mcp/handlers/index.ts +43 -0
- package/src/mcp/handlers/job.handler.test.ts +189 -0
- package/src/mcp/handlers/job.handler.ts +116 -0
- package/src/mcp/handlers/search.handler.test.ts +334 -0
- package/src/mcp/handlers/search.handler.ts +209 -0
- package/src/mcp/handlers/store.handler.test.ts +415 -0
- package/src/mcp/handlers/store.handler.ts +295 -0
- package/src/mcp/schemas/index.test.ts +315 -0
- package/src/mcp/schemas/index.ts +138 -0
- package/src/mcp/server.test.ts +36 -0
- package/src/mcp/server.ts +162 -0
- package/src/mcp/types.ts +41 -0
- package/src/plugin/commands.test.ts +789 -0
- package/src/plugin/commands.ts +257 -0
- package/src/plugin/dependency-analyzer.test.ts +380 -0
- package/src/plugin/dependency-analyzer.ts +147 -0
- package/src/plugin/git-clone.test.ts +332 -0
- package/src/plugin/git-clone.ts +57 -0
- package/src/server/app.test.ts +752 -0
- package/src/server/app.ts +119 -0
- package/src/server/index.test.ts +477 -0
- package/src/server/index.ts +1 -0
- package/src/services/chunking.service.test.ts +363 -0
- package/src/services/chunking.service.ts +350 -0
- package/src/services/code-graph.service.test.ts +304 -0
- package/src/services/code-graph.service.ts +302 -0
- package/src/services/code-unit.service.test.ts +596 -0
- package/src/services/code-unit.service.ts +115 -0
- package/src/services/config.service.test.ts +127 -0
- package/src/services/config.service.ts +69 -0
- package/src/services/index.service.test.ts +1002 -0
- package/src/services/index.service.ts +266 -0
- package/src/services/index.ts +75 -0
- package/src/services/job.service.test.ts +418 -0
- package/src/services/job.service.ts +246 -0
- package/src/services/project-root.service.test.ts +506 -0
- package/src/services/project-root.service.ts +112 -0
- package/src/services/search.service.test.ts +1105 -0
- package/src/services/search.service.ts +892 -0
- package/src/services/services.test.ts +38 -0
- package/src/services/snippet.service.test.ts +205 -0
- package/src/services/snippet.service.ts +166 -0
- package/src/services/store.service.test.ts +474 -0
- package/src/services/store.service.ts +225 -0
- package/src/services/watch.service.test.ts +553 -0
- package/src/services/watch.service.ts +71 -0
- package/src/types/brands.test.ts +45 -0
- package/src/types/brands.ts +32 -0
- package/src/types/config.ts +79 -0
- package/src/types/document.ts +30 -0
- package/src/types/index.ts +66 -0
- package/src/types/job.ts +46 -0
- package/src/types/progress.ts +9 -0
- package/src/types/result.test.ts +44 -0
- package/src/types/result.ts +41 -0
- package/src/types/search.ts +95 -0
- package/src/types/store.test.ts +69 -0
- package/src/types/store.ts +47 -0
- package/src/utils/type-guards.test.ts +346 -0
- package/src/utils/type-guards.ts +61 -0
- package/src/workers/background-worker-cli.ts +105 -0
- package/src/workers/background-worker.test.ts +178 -0
- package/src/workers/background-worker.ts +294 -0
- package/src/workers/spawn-worker.test.ts +128 -0
- package/src/workers/spawn-worker.ts +49 -0
- package/tests/analysis/ast-parser.test.ts +98 -0
- package/tests/analysis/code-graph.test.ts +60 -0
- package/tests/fixtures/README.md +114 -0
- package/tests/fixtures/code-snippets/api/error-handling.ts +267 -0
- package/tests/fixtures/code-snippets/api/rest-controller.ts +303 -0
- package/tests/fixtures/code-snippets/auth/jwt-auth.ts +213 -0
- package/tests/fixtures/code-snippets/auth/oauth-flow.ts +245 -0
- package/tests/fixtures/code-snippets/database/repository-pattern.ts +272 -0
- package/tests/fixtures/corpus/VERSION.md +25 -0
- package/tests/fixtures/corpus/articles/jwt-authentication.md +97 -0
- package/tests/fixtures/corpus/articles/react-hooks-patterns.md +127 -0
- package/tests/fixtures/corpus/articles/typescript-generics.md +111 -0
- package/tests/fixtures/corpus/documentation/express-middleware.md +71 -0
- package/tests/fixtures/corpus/documentation/express-routing.md +83 -0
- package/tests/fixtures/corpus/documentation/node-streams.md +78 -0
- package/tests/fixtures/corpus/oss-repos/express/History.md +3871 -0
- package/tests/fixtures/corpus/oss-repos/express/LICENSE +24 -0
- package/tests/fixtures/corpus/oss-repos/express/Readme.md +276 -0
- package/tests/fixtures/corpus/oss-repos/express/SECURITY.md +56 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/Makefile +17 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/README.md +34 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/middleware.js +20 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/run +18 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/README.md +29 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/index.js +134 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/views/foot.ejs +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/views/head.ejs +20 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/views/login.ejs +21 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/content-negotiation/db.js +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/content-negotiation/index.js +46 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/content-negotiation/users.js +19 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/cookie-sessions/index.js +25 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/cookies/index.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/files/CCTV/345/244/247/350/265/233/344/270/212/346/265/267/345/210/206/350/265/233/345/214/272.txt +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/files/amazing.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/files/notes/groceries.txt +3 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/index.js +40 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/index.js +57 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/public/stylesheets/style.css +4 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/views/footer.html +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/views/header.html +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/views/users.html +10 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error/index.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/index.js +103 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/404.ejs +3 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/500.ejs +8 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/error_header.ejs +10 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/footer.ejs +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/index.ejs +20 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/hello-world/index.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/markdown/index.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/markdown/views/index.md +4 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/multi-router/controllers/api_v1.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/multi-router/controllers/api_v2.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/multi-router/index.js +18 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/main/index.js +5 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/pet/index.js +31 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/pet/views/edit.ejs +17 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/pet/views/show.ejs +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/index.js +41 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/views/edit.hbs +27 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/views/list.hbs +18 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/views/show.hbs +31 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user-pet/index.js +22 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/db.js +16 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/index.js +95 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/lib/boot.js +83 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/public/style.css +14 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/views/404.ejs +13 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/views/5xx.ejs +13 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/online/index.js +61 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/params/index.js +74 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/resource/index.js +95 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-map/index.js +75 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-middleware/index.js +90 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/index.js +55 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/post.js +13 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/public/style.css +24 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/site.js +5 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/user.js +47 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/footer.ejs +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/header.ejs +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/index.ejs +10 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/posts/index.ejs +12 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/users/edit.ejs +23 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/users/index.ejs +14 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/users/view.ejs +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/search/index.js +61 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/search/public/client.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/search/public/index.html +21 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/session/index.js +37 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/session/redis.js +39 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/index.js +43 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/public/css/style.css +3 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/public/hello.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/public/js/app.js +1 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/vhost/index.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-constructor/github-view.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-constructor/index.js +48 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-locals/index.js +155 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-locals/user.js +36 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-locals/views/index.ejs +20 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/web-service/index.js +117 -0
- package/tests/fixtures/corpus/oss-repos/express/index.js +11 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/application.js +631 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/express.js +81 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/request.js +514 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/response.js +1053 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/utils.js +271 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/view.js +205 -0
- package/tests/fixtures/corpus/oss-repos/express/package.json +99 -0
- package/tests/fixtures/corpus/oss-repos/express/test/Route.js +274 -0
- package/tests/fixtures/corpus/oss-repos/express/test/Router.js +636 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/auth.js +117 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/content-negotiation.js +49 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/cookie-sessions.js +38 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/cookies.js +71 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/downloads.js +47 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/ejs.js +17 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/error-pages.js +99 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/error.js +29 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/hello-world.js +21 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/markdown.js +21 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/multi-router.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/mvc.js +132 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/params.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/resource.js +68 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/route-map.js +45 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/route-separation.js +97 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/vhost.js +46 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/web-service.js +105 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.all.js +38 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.engine.js +83 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.head.js +66 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.js +120 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.listen.js +55 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.locals.js +26 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.options.js +116 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.param.js +323 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.render.js +374 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.request.js +143 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.response.js +143 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.route.js +197 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.router.js +1217 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.routes.error.js +62 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.use.js +542 -0
- package/tests/fixtures/corpus/oss-repos/express/test/config.js +207 -0
- package/tests/fixtures/corpus/oss-repos/express/test/exports.js +82 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.json.js +755 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.raw.js +513 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.static.js +815 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.text.js +566 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.urlencoded.js +828 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/% of dogs.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/.name +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/blog/index.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/blog/post/index.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/broken.send +0 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/default_layout/name.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/default_layout/user.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/email.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/empty.txt +0 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/local_layout/user.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/name.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/name.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/nums.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/pets/names.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/snow /342/230/203/.gitkeep +0 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/todo.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/todo.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/user.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/user.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/users/index.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/users/tobi.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/middleware.basic.js +42 -0
- package/tests/fixtures/corpus/oss-repos/express/test/regression.js +20 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.accepts.js +125 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.acceptsCharsets.js +50 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.acceptsEncodings.js +39 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.acceptsLanguages.js +57 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.baseUrl.js +88 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.fresh.js +70 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.get.js +60 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.host.js +156 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.hostname.js +188 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.ip.js +113 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.ips.js +71 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.is.js +169 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.path.js +20 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.protocol.js +113 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.query.js +106 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.range.js +104 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.route.js +28 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.secure.js +101 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.signedCookies.js +37 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.stale.js +50 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.subdomains.js +173 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.xhr.js +42 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.append.js +116 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.attachment.js +79 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.clearCookie.js +62 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.cookie.js +295 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.download.js +487 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.format.js +248 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.get.js +21 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.json.js +186 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.jsonp.js +344 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.links.js +65 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.locals.js +40 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.location.js +316 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.redirect.js +214 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.render.js +367 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.send.js +569 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.sendFile.js +913 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.sendStatus.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.set.js +124 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.status.js +206 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.type.js +46 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.vary.js +90 -0
- package/tests/fixtures/corpus/oss-repos/express/test/support/env.js +3 -0
- package/tests/fixtures/corpus/oss-repos/express/test/support/tmpl.js +36 -0
- package/tests/fixtures/corpus/oss-repos/express/test/support/utils.js +86 -0
- package/tests/fixtures/corpus/oss-repos/express/test/utils.js +83 -0
- package/tests/fixtures/corpus/oss-repos/hono/.devcontainer/Dockerfile +11 -0
- package/tests/fixtures/corpus/oss-repos/hono/.devcontainer/devcontainer.json +21 -0
- package/tests/fixtures/corpus/oss-repos/hono/.devcontainer/docker-compose.yml +18 -0
- package/tests/fixtures/corpus/oss-repos/hono/.eslintignore +1 -0
- package/tests/fixtures/corpus/oss-repos/hono/.eslintrc.cjs +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/.gitpod.yml +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/.prettierrc +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/.vitest.config/jsx-runtime-default.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/.vitest.config/jsx-runtime-dom.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/.vitest.config/setup-vitest.ts +47 -0
- package/tests/fixtures/corpus/oss-repos/hono/LICENSE +21 -0
- package/tests/fixtures/corpus/oss-repos/hono/README.md +91 -0
- package/tests/fixtures/corpus/oss-repos/hono/build.ts +80 -0
- package/tests/fixtures/corpus/oss-repos/hono/bun.lockb +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/bunfig.toml +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/codecov.yml +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/CODE_OF_CONDUCT.md +128 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/CONTRIBUTING.md +62 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/MIGRATION.md +295 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-logo.png +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-logo.pxm +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-logo.svg +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-title.png +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-title.pxm +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/jsr.json +119 -0
- package/tests/fixtures/corpus/oss-repos/hono/package.cjs.json +3 -0
- package/tests/fixtures/corpus/oss-repos/hono/package.json +650 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +492 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/index.ts +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/types.ts +144 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/conninfo.ts +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/index.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/serve-static.ts +35 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/server.ts +30 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/ssg.ts +27 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/websocket.ts +110 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +120 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/conninfo.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static-module.ts +12 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +39 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/utils.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/websocket.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/conninfo.ts +17 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/deno.d.ts +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/index.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/serve-static.ts +40 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/ssg.ts +27 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/websocket.ts +51 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/lambda-edge/conninfo.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/lambda-edge/handler.ts +189 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/lambda-edge/index.ts +14 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/netlify/handler.ts +10 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/netlify/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/netlify/mod.ts +1 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/service-worker/handler.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/service-worker/index.ts +5 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/service-worker/types.ts +14 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/vercel/conninfo.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/vercel/handler.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/vercel/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +214 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/index.ts +14 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +180 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/utils.ts +54 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/compose.ts +94 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +914 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +81 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +85 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/conninfo/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/conninfo/types.ts +45 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/cookie/index.ts +130 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/common.ts +243 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +220 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/dev/index.ts +79 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +246 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/html/index.ts +56 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/index.ts +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/middleware.ts +79 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +388 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/utils.ts +71 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/index.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/sse.ts +89 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/stream.ts +36 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/text.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/testing/index.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/websocket/index.ts +57 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +523 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/http-exception.ts +78 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/index.ts +51 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/base.ts +419 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/children.ts +20 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/components.ts +195 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/constants.ts +5 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/context.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/client.ts +89 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/components.ts +39 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/context.ts +52 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +246 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/hooks/index.ts +91 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/index.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +398 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/jsx-dev-runtime.ts +22 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/jsx-runtime.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +772 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/server.ts +70 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/utils.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +426 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/index.ts +114 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/common.ts +11 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +196 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-elements.ts +924 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/jsx-dev-runtime.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/jsx-runtime.ts +18 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/streaming.ts +184 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/types.ts +41 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +36 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/basic-auth/index.ts +128 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/bearer-auth/index.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/body-limit/index.ts +115 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/cache/index.ts +127 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/combine/index.ts +153 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/compress/index.ts +79 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/context-storage/index.ts +55 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/cors/index.ts +141 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/csrf/index.ts +90 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/etag/index.ts +88 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/ip-restriction/index.ts +178 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +158 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jwt/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jwt/jwt.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/logger/index.ts +93 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/method-override/index.ts +146 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/powered-by/index.ts +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/pretty-json/index.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/request-id/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/request-id/request-id.ts +59 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/secure-headers/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/secure-headers/permissions-policy.ts +86 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/secure-headers/secure-headers.ts +319 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +140 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/timeout/index.ts +58 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/timing/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/timing/timing.ts +225 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/trailing-slash/index.ts +71 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +24 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +20 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/request.ts +403 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/linear-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/linear-router/router.ts +132 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +54 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +274 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/trie.ts +74 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/smart-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/smart-router/router.ts +69 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +205 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/router.ts +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router.ts +103 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +2009 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/basic-auth.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +225 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/buffer.ts +65 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/concurrent.ts +55 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +230 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/crypto.ts +65 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/filepath.ts +56 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/handler.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/html.ts +182 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/http-status.ts +69 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/ipaddr.ts +113 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/jwa.ts +23 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/jws.ts +226 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/jwt.ts +114 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/types.ts +83 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/utf8.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/mime.ts +142 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/stream.ts +96 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +105 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/url.ts +310 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +151 -0
- package/tests/fixtures/corpus/oss-repos/hono/tsconfig.build.json +23 -0
- package/tests/fixtures/corpus/oss-repos/hono/tsconfig.json +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/vitest.config.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/yarn.lock +6232 -0
- package/tests/fixtures/documentation/api-reference.md +412 -0
- package/tests/fixtures/documentation/architecture.md +214 -0
- package/tests/fixtures/documentation/deployment-guide.md +420 -0
- package/tests/fixtures/github-readmes/express.md +133 -0
- package/tests/fixtures/github-readmes/nextjs.md +106 -0
- package/tests/fixtures/github-readmes/react.md +74 -0
- package/tests/fixtures/github-readmes/typescript.md +93 -0
- package/tests/fixtures/github-readmes/vite.md +79 -0
- package/tests/fixtures/queries/core.json +125 -0
- package/tests/fixtures/queries/extended.json +427 -0
- package/tests/fixtures/queries/generated/.gitkeep +0 -0
- package/tests/fixtures/test-server.ts +267 -0
- package/tests/helpers/performance-metrics.ts +387 -0
- package/tests/helpers/search-relevance.ts +381 -0
- package/tests/integration/cli-consistency.test.ts +299 -0
- package/tests/integration/cli.test.ts +69 -0
- package/tests/integration/e2e-workflow.test.ts +612 -0
- package/tests/integration/python-bridge.test.ts +183 -0
- package/tests/integration/search-quality.test.ts +718 -0
- package/tests/integration/stress.test.ts +326 -0
- package/tests/mcp/server.test.ts +15 -0
- package/tests/scripts/schemas/evaluation.json +44 -0
- package/tests/scripts/schemas/query-generation.json +21 -0
- package/tests/services/code-unit.service.test.ts +47 -0
- package/tests/services/search.progressive-context.test.ts +35 -0
- package/tsconfig.json +34 -0
- package/tsup.config.ts +15 -0
- package/turndown-plugin-gfm.d.ts +29 -0
- package/vitest.config.ts +79 -0
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive tests for IntelligentCrawler
|
|
3
|
+
* Coverage: mode selection, network errors, loop prevention, memory, events, stop, limits, domain filtering
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
7
|
+
import { IntelligentCrawler } from './intelligent-crawler.js';
|
|
8
|
+
import type { CrawlProgress } from './intelligent-crawler.js';
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import * as articleConverter from './article-converter.js';
|
|
11
|
+
|
|
12
|
+
// Mock dependencies
|
|
13
|
+
vi.mock('axios');
|
|
14
|
+
vi.mock('./claude-client.js');
|
|
15
|
+
vi.mock('./bridge.js');
|
|
16
|
+
vi.mock('./article-converter.js');
|
|
17
|
+
|
|
18
|
+
// Import mocked classes after mocking
|
|
19
|
+
const { ClaudeClient } = await import('./claude-client.js');
|
|
20
|
+
const { PythonBridge } = await import('./bridge.js');
|
|
21
|
+
|
|
22
|
+
describe('IntelligentCrawler', () => {
|
|
23
|
+
let crawler: IntelligentCrawler;
|
|
24
|
+
let mockClaudeClient: any;
|
|
25
|
+
let mockPythonBridge: any;
|
|
26
|
+
let progressEvents: CrawlProgress[];
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
// Reset all mocks
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
progressEvents = [];
|
|
32
|
+
|
|
33
|
+
// Setup ClaudeClient mock - ensure isAvailable returns true for intelligent mode tests
|
|
34
|
+
mockClaudeClient = {
|
|
35
|
+
determineCrawlUrls: vi.fn(),
|
|
36
|
+
extractContent: vi.fn(),
|
|
37
|
+
};
|
|
38
|
+
vi.mocked(ClaudeClient).mockImplementation(function() { return mockClaudeClient; });
|
|
39
|
+
// Mock static isAvailable to return true (Claude CLI is available in tests)
|
|
40
|
+
vi.mocked(ClaudeClient.isAvailable).mockReturnValue(true);
|
|
41
|
+
|
|
42
|
+
// Setup PythonBridge mock
|
|
43
|
+
mockPythonBridge = {
|
|
44
|
+
crawl: vi.fn(),
|
|
45
|
+
fetchHeadless: vi.fn(),
|
|
46
|
+
stop: vi.fn().mockResolvedValue(undefined),
|
|
47
|
+
};
|
|
48
|
+
vi.mocked(PythonBridge).mockImplementation(function() { return mockPythonBridge; });
|
|
49
|
+
|
|
50
|
+
// Setup axios mock
|
|
51
|
+
vi.mocked(axios.get).mockResolvedValue({
|
|
52
|
+
data: '<html><body><h1>Test</h1><a href="https://example.com/page1">Link</a></body></html>',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Setup convertHtmlToMarkdown mock
|
|
56
|
+
vi.mocked(articleConverter.convertHtmlToMarkdown).mockResolvedValue({
|
|
57
|
+
success: true,
|
|
58
|
+
markdown: '# Test\n\nContent',
|
|
59
|
+
title: 'Test Page',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Create crawler instance
|
|
63
|
+
crawler = new IntelligentCrawler();
|
|
64
|
+
|
|
65
|
+
// Listen to progress events
|
|
66
|
+
crawler.on('progress', (progress: CrawlProgress) => {
|
|
67
|
+
progressEvents.push(progress);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(async () => {
|
|
72
|
+
await crawler.stop();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Mode Selection', () => {
|
|
76
|
+
it('should use intelligent mode when crawlInstruction is provided', async () => {
|
|
77
|
+
mockClaudeClient.determineCrawlUrls.mockResolvedValue({
|
|
78
|
+
urls: ['https://example.com/page1'],
|
|
79
|
+
reasoning: 'Found relevant page',
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const results = [];
|
|
83
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
84
|
+
crawlInstruction: 'Find all docs',
|
|
85
|
+
})) {
|
|
86
|
+
results.push(result);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
expect(mockClaudeClient.determineCrawlUrls).toHaveBeenCalledOnce();
|
|
90
|
+
expect(results).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should use simple mode when crawlInstruction is empty string', async () => {
|
|
94
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
95
|
+
pages: [{ links: [] }],
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const results = [];
|
|
99
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
100
|
+
crawlInstruction: '',
|
|
101
|
+
})) {
|
|
102
|
+
results.push(result);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
expect(mockClaudeClient.determineCrawlUrls).not.toHaveBeenCalled();
|
|
106
|
+
expect(results).toHaveLength(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should use simple mode when crawlInstruction is undefined', async () => {
|
|
110
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
111
|
+
pages: [{ links: [] }],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const results = [];
|
|
115
|
+
for await (const result of crawler.crawl('https://example.com', {})) {
|
|
116
|
+
results.push(result);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
expect(mockClaudeClient.determineCrawlUrls).not.toHaveBeenCalled();
|
|
120
|
+
expect(results).toHaveLength(1);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should force simple mode when simple option is true', async () => {
|
|
124
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
125
|
+
pages: [{ links: [] }],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const results = [];
|
|
129
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
130
|
+
crawlInstruction: 'Find all docs',
|
|
131
|
+
simple: true,
|
|
132
|
+
})) {
|
|
133
|
+
results.push(result);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
expect(mockClaudeClient.determineCrawlUrls).not.toHaveBeenCalled();
|
|
137
|
+
expect(results).toHaveLength(1);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('Network Error Handling', () => {
|
|
142
|
+
it('should handle timeout errors gracefully', async () => {
|
|
143
|
+
vi.mocked(axios.get).mockRejectedValueOnce({
|
|
144
|
+
code: 'ETIMEDOUT',
|
|
145
|
+
message: 'timeout of 30000ms exceeded',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const results = [];
|
|
149
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
150
|
+
results.push(result);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Should fail to fetch and emit error but not crash
|
|
154
|
+
expect(results).toHaveLength(0);
|
|
155
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
156
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should handle DNS failures', async () => {
|
|
160
|
+
vi.mocked(axios.get).mockRejectedValueOnce({
|
|
161
|
+
code: 'ENOTFOUND',
|
|
162
|
+
message: 'getaddrinfo ENOTFOUND invalid.example.com',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const results = [];
|
|
166
|
+
for await (const result of crawler.crawl('https://invalid.example.com', { simple: true })) {
|
|
167
|
+
results.push(result);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
expect(results).toHaveLength(0);
|
|
171
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
172
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should handle 404 errors', async () => {
|
|
176
|
+
vi.mocked(axios.get).mockRejectedValueOnce({
|
|
177
|
+
response: { status: 404 },
|
|
178
|
+
message: 'Request failed with status code 404',
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const results = [];
|
|
182
|
+
for await (const result of crawler.crawl('https://example.com/404', { simple: true })) {
|
|
183
|
+
results.push(result);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
expect(results).toHaveLength(0);
|
|
187
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
188
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should handle 500 errors', async () => {
|
|
192
|
+
vi.mocked(axios.get).mockRejectedValueOnce({
|
|
193
|
+
response: { status: 500 },
|
|
194
|
+
message: 'Request failed with status code 500',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const results = [];
|
|
198
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
199
|
+
results.push(result);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
expect(results).toHaveLength(0);
|
|
203
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
204
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should handle network connection refused', async () => {
|
|
208
|
+
vi.mocked(axios.get).mockRejectedValueOnce({
|
|
209
|
+
code: 'ECONNREFUSED',
|
|
210
|
+
message: 'connect ECONNREFUSED 127.0.0.1:80',
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const results = [];
|
|
214
|
+
for await (const result of crawler.crawl('http://localhost', { simple: true })) {
|
|
215
|
+
results.push(result);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
expect(results).toHaveLength(0);
|
|
219
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
220
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('Infinite Loop Prevention', () => {
|
|
225
|
+
it('should not visit the same URL twice in simple mode', async () => {
|
|
226
|
+
const circular = 'https://example.com/page1';
|
|
227
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
228
|
+
pages: [{ links: [circular] }], // Link back to itself
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
vi.mocked(axios.get)
|
|
232
|
+
.mockResolvedValueOnce({ data: `<html><body><a href="${circular}">Self</a></body></html>` });
|
|
233
|
+
|
|
234
|
+
const results = [];
|
|
235
|
+
for await (const result of crawler.crawl(circular, { simple: true, maxPages: 10 })) {
|
|
236
|
+
results.push(result);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Should only crawl once despite circular link
|
|
240
|
+
expect(results).toHaveLength(1);
|
|
241
|
+
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should not visit the same URL twice in intelligent mode', async () => {
|
|
245
|
+
const duplicate = 'https://example.com/page1';
|
|
246
|
+
mockClaudeClient.determineCrawlUrls.mockResolvedValue({
|
|
247
|
+
urls: [duplicate, duplicate, duplicate], // Same URL repeated
|
|
248
|
+
reasoning: 'Found duplicate URLs',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const results = [];
|
|
252
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
253
|
+
crawlInstruction: 'Find all',
|
|
254
|
+
maxPages: 10,
|
|
255
|
+
})) {
|
|
256
|
+
results.push(result);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Should only crawl the URL once
|
|
260
|
+
expect(results).toHaveLength(1);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should handle circular references across multiple pages', async () => {
|
|
264
|
+
const page1 = 'https://example.com/page1';
|
|
265
|
+
const page2 = 'https://example.com/page2';
|
|
266
|
+
|
|
267
|
+
mockPythonBridge.crawl
|
|
268
|
+
.mockResolvedValueOnce({ pages: [{ links: [page2] }] }) // page1 -> page2
|
|
269
|
+
.mockResolvedValueOnce({ pages: [{ links: [page1] }] }); // page2 -> page1
|
|
270
|
+
|
|
271
|
+
vi.mocked(axios.get)
|
|
272
|
+
.mockResolvedValueOnce({ data: `<html><body><a href="${page2}">Next</a></body></html>` })
|
|
273
|
+
.mockResolvedValueOnce({ data: `<html><body><a href="${page1}">Back</a></body></html>` });
|
|
274
|
+
|
|
275
|
+
const results = [];
|
|
276
|
+
for await (const result of crawler.crawl(page1, { simple: true, maxPages: 10 })) {
|
|
277
|
+
results.push(result);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Should crawl both pages once, not infinitely
|
|
281
|
+
expect(results).toHaveLength(2);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe('Memory Management', () => {
|
|
286
|
+
it('should track visited URLs in a Set', async () => {
|
|
287
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
288
|
+
|
|
289
|
+
const results = [];
|
|
290
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
291
|
+
results.push(result);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
expect(results).toHaveLength(1);
|
|
295
|
+
// Verify visited set is working by attempting to crawl again
|
|
296
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
297
|
+
results.push(result);
|
|
298
|
+
}
|
|
299
|
+
// Second crawl should also work (visited set cleared)
|
|
300
|
+
expect(results).toHaveLength(2);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should clear visited set on new crawl', async () => {
|
|
304
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
305
|
+
|
|
306
|
+
// First crawl
|
|
307
|
+
const results1 = [];
|
|
308
|
+
for await (const result of crawler.crawl('https://example.com/a', { simple: true })) {
|
|
309
|
+
results1.push(result);
|
|
310
|
+
}
|
|
311
|
+
expect(results1).toHaveLength(1);
|
|
312
|
+
|
|
313
|
+
// Second crawl with same URL should work (set was cleared)
|
|
314
|
+
const results2 = [];
|
|
315
|
+
for await (const result of crawler.crawl('https://example.com/a', { simple: true })) {
|
|
316
|
+
results2.push(result);
|
|
317
|
+
}
|
|
318
|
+
expect(results2).toHaveLength(1);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe('Event Emission', () => {
|
|
323
|
+
it('should emit start event', async () => {
|
|
324
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
325
|
+
|
|
326
|
+
const results = [];
|
|
327
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true, maxPages: 10 })) {
|
|
328
|
+
results.push(result);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const startEvents = progressEvents.filter((e) => e.type === 'start');
|
|
332
|
+
expect(startEvents).toHaveLength(1);
|
|
333
|
+
expect(startEvents[0]?.pagesVisited).toBe(0);
|
|
334
|
+
expect(startEvents[0]?.totalPages).toBe(10);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should emit page events', async () => {
|
|
338
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
339
|
+
|
|
340
|
+
const results = [];
|
|
341
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
342
|
+
results.push(result);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const pageEvents = progressEvents.filter((e) => e.type === 'page');
|
|
346
|
+
expect(pageEvents.length).toBeGreaterThan(0);
|
|
347
|
+
expect(pageEvents[0]?.currentUrl).toBe('https://example.com');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should emit complete event', async () => {
|
|
351
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
352
|
+
|
|
353
|
+
const results = [];
|
|
354
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
355
|
+
results.push(result);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const completeEvents = progressEvents.filter((e) => e.type === 'complete');
|
|
359
|
+
expect(completeEvents).toHaveLength(1);
|
|
360
|
+
expect(completeEvents[0]?.pagesVisited).toBe(1);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should emit strategy events in intelligent mode', async () => {
|
|
364
|
+
mockClaudeClient.determineCrawlUrls.mockResolvedValue({
|
|
365
|
+
urls: ['https://example.com/page1'],
|
|
366
|
+
reasoning: 'Strategy reasoning',
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const results = [];
|
|
370
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
371
|
+
crawlInstruction: 'Find all',
|
|
372
|
+
})) {
|
|
373
|
+
results.push(result);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const strategyEvents = progressEvents.filter((e) => e.type === 'strategy');
|
|
377
|
+
expect(strategyEvents.length).toBeGreaterThanOrEqual(1);
|
|
378
|
+
expect(strategyEvents.some((e) => e.message?.includes('Strategy reasoning'))).toBe(true);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should emit extraction events when extract instruction provided', async () => {
|
|
382
|
+
mockClaudeClient.extractContent.mockResolvedValue('Extracted content');
|
|
383
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
384
|
+
|
|
385
|
+
const results = [];
|
|
386
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
387
|
+
simple: true,
|
|
388
|
+
extractInstruction: 'Extract pricing',
|
|
389
|
+
})) {
|
|
390
|
+
results.push(result);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const extractionEvents = progressEvents.filter((e) => e.type === 'extraction');
|
|
394
|
+
expect(extractionEvents.length).toBeGreaterThan(0);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should emit error events on page fetch failures', async () => {
|
|
398
|
+
vi.mocked(axios.get).mockRejectedValueOnce(new Error('Network error'));
|
|
399
|
+
|
|
400
|
+
const results = [];
|
|
401
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
402
|
+
results.push(result);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
406
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
407
|
+
expect(errorEvents[0]?.error).toBeDefined();
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe('Stop Functionality', () => {
|
|
412
|
+
it('should stop crawling when stop is called', async () => {
|
|
413
|
+
mockPythonBridge.crawl.mockImplementation(async () => {
|
|
414
|
+
// Simulate slow crawl
|
|
415
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
416
|
+
return { pages: [{ links: ['https://example.com/next'] }] };
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const results = [];
|
|
420
|
+
const crawlPromise = (async () => {
|
|
421
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true, maxPages: 100 })) {
|
|
422
|
+
results.push(result);
|
|
423
|
+
}
|
|
424
|
+
})();
|
|
425
|
+
|
|
426
|
+
// Stop after a short delay
|
|
427
|
+
setTimeout(() => crawler.stop(), 50);
|
|
428
|
+
|
|
429
|
+
await crawlPromise;
|
|
430
|
+
|
|
431
|
+
// Should have stopped early
|
|
432
|
+
expect(results.length).toBeLessThan(100);
|
|
433
|
+
expect(mockPythonBridge.stop).toHaveBeenCalled();
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should reset stopped flag on new crawl', async () => {
|
|
437
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
438
|
+
|
|
439
|
+
// First crawl with stop
|
|
440
|
+
await crawler.stop();
|
|
441
|
+
|
|
442
|
+
// Second crawl should work
|
|
443
|
+
const results = [];
|
|
444
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
445
|
+
results.push(result);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
expect(results).toHaveLength(1);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('Max Pages Enforcement', () => {
|
|
453
|
+
it('should respect maxPages limit in simple mode', async () => {
|
|
454
|
+
const links = Array.from({ length: 20 }, (_, i) => `https://example.com/page${i}`);
|
|
455
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links }] });
|
|
456
|
+
|
|
457
|
+
const results = [];
|
|
458
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true, maxPages: 3 })) {
|
|
459
|
+
results.push(result);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
expect(results.length).toBeLessThanOrEqual(3);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should respect maxPages limit in intelligent mode', async () => {
|
|
466
|
+
const urls = Array.from({ length: 20 }, (_, i) => `https://example.com/page${i}`);
|
|
467
|
+
mockClaudeClient.determineCrawlUrls.mockResolvedValue({
|
|
468
|
+
urls,
|
|
469
|
+
reasoning: 'Found many pages',
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const results = [];
|
|
473
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
474
|
+
crawlInstruction: 'Find all',
|
|
475
|
+
maxPages: 5,
|
|
476
|
+
})) {
|
|
477
|
+
results.push(result);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
expect(results.length).toBeLessThanOrEqual(5);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should use default maxPages of 50', async () => {
|
|
484
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
485
|
+
|
|
486
|
+
const results = [];
|
|
487
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
488
|
+
results.push(result);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const startEvent = progressEvents.find((e) => e.type === 'start');
|
|
492
|
+
expect(startEvent?.totalPages).toBe(50);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
describe('Same-Domain Filtering Logic', () => {
|
|
497
|
+
it('should filter out different domain links in simple mode', async () => {
|
|
498
|
+
mockPythonBridge.crawl
|
|
499
|
+
.mockResolvedValueOnce({
|
|
500
|
+
pages: [
|
|
501
|
+
{
|
|
502
|
+
links: [
|
|
503
|
+
'https://example.com/page1', // Same domain
|
|
504
|
+
'https://other.com/page2', // Different domain
|
|
505
|
+
],
|
|
506
|
+
},
|
|
507
|
+
],
|
|
508
|
+
})
|
|
509
|
+
.mockResolvedValueOnce({ pages: [{ links: [] }] });
|
|
510
|
+
|
|
511
|
+
vi.mocked(axios.get)
|
|
512
|
+
.mockResolvedValueOnce({ data: '<html><body>Seed</body></html>' })
|
|
513
|
+
.mockResolvedValueOnce({ data: '<html><body>Page1</body></html>' });
|
|
514
|
+
|
|
515
|
+
const results = [];
|
|
516
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true, maxPages: 10 })) {
|
|
517
|
+
results.push(result);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Should only crawl same-domain link
|
|
521
|
+
expect(results).toHaveLength(2); // Seed + page1
|
|
522
|
+
expect(results.some((r) => r.url.includes('other.com'))).toBe(false);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('should allow subdomain links', async () => {
|
|
526
|
+
mockPythonBridge.crawl
|
|
527
|
+
.mockResolvedValueOnce({
|
|
528
|
+
pages: [{ links: ['https://docs.example.com/page1'] }],
|
|
529
|
+
})
|
|
530
|
+
.mockResolvedValueOnce({ pages: [{ links: [] }] });
|
|
531
|
+
|
|
532
|
+
vi.mocked(axios.get)
|
|
533
|
+
.mockResolvedValueOnce({ data: '<html><body>Seed</body></html>' })
|
|
534
|
+
.mockResolvedValueOnce({ data: '<html><body>Page1</body></html>' });
|
|
535
|
+
|
|
536
|
+
const results = [];
|
|
537
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true, maxPages: 10 })) {
|
|
538
|
+
results.push(result);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
expect(results).toHaveLength(2);
|
|
542
|
+
expect(results.some((r) => r.url.includes('docs.example.com'))).toBe(true);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('should handle invalid URLs in link extraction', async () => {
|
|
546
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
547
|
+
pages: [
|
|
548
|
+
{
|
|
549
|
+
links: [
|
|
550
|
+
'not-a-valid-url',
|
|
551
|
+
'javascript:void(0)',
|
|
552
|
+
'mailto:test@example.com',
|
|
553
|
+
],
|
|
554
|
+
},
|
|
555
|
+
],
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const results = [];
|
|
559
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
560
|
+
results.push(result);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Should only crawl the seed URL
|
|
564
|
+
expect(results).toHaveLength(1);
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
describe('Intelligent Mode Fallback', () => {
|
|
569
|
+
it('should fallback to simple mode when Claude strategy fails', async () => {
|
|
570
|
+
mockClaudeClient.determineCrawlUrls.mockRejectedValue(new Error('Claude API error'));
|
|
571
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
572
|
+
|
|
573
|
+
const results = [];
|
|
574
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
575
|
+
crawlInstruction: 'Find all docs',
|
|
576
|
+
})) {
|
|
577
|
+
results.push(result);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Should still crawl using simple mode
|
|
581
|
+
expect(results).toHaveLength(1);
|
|
582
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
583
|
+
expect(errorEvents.some((e) => e.message?.includes('falling back to simple mode'))).toBe(true);
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
describe('Content Extraction', () => {
|
|
588
|
+
it('should extract content when extractInstruction provided', async () => {
|
|
589
|
+
mockClaudeClient.extractContent.mockResolvedValue('Extracted pricing info');
|
|
590
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
591
|
+
|
|
592
|
+
const results = [];
|
|
593
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
594
|
+
simple: true,
|
|
595
|
+
extractInstruction: 'Extract pricing',
|
|
596
|
+
})) {
|
|
597
|
+
results.push(result);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
expect(results).toHaveLength(1);
|
|
601
|
+
expect(results[0]?.extracted).toBe('Extracted pricing info');
|
|
602
|
+
expect(mockClaudeClient.extractContent).toHaveBeenCalledWith(
|
|
603
|
+
expect.any(String),
|
|
604
|
+
'Extract pricing',
|
|
605
|
+
);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('should continue without extraction if extraction fails', async () => {
|
|
609
|
+
mockClaudeClient.extractContent.mockRejectedValue(new Error('Extraction failed'));
|
|
610
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
611
|
+
|
|
612
|
+
const results = [];
|
|
613
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
614
|
+
simple: true,
|
|
615
|
+
extractInstruction: 'Extract pricing',
|
|
616
|
+
})) {
|
|
617
|
+
results.push(result);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
expect(results).toHaveLength(1);
|
|
621
|
+
expect(results[0]?.extracted).toBeUndefined();
|
|
622
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
623
|
+
expect(errorEvents.some((e) => e.message?.includes('storing raw markdown'))).toBe(true);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it('should not extract when extractInstruction is empty', async () => {
|
|
627
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
628
|
+
|
|
629
|
+
const results = [];
|
|
630
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
631
|
+
simple: true,
|
|
632
|
+
extractInstruction: '',
|
|
633
|
+
})) {
|
|
634
|
+
results.push(result);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
expect(results).toHaveLength(1);
|
|
638
|
+
expect(results[0]?.extracted).toBeUndefined();
|
|
639
|
+
expect(mockClaudeClient.extractContent).not.toHaveBeenCalled();
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
describe('Link Extraction Error Handling', () => {
|
|
644
|
+
it('should throw error when Python bridge fails', async () => {
|
|
645
|
+
mockPythonBridge.crawl.mockRejectedValue(new Error('Network timeout'));
|
|
646
|
+
|
|
647
|
+
const crawler = new IntelligentCrawler();
|
|
648
|
+
// Access private property for testing
|
|
649
|
+
(crawler as any).pythonBridge = mockPythonBridge;
|
|
650
|
+
|
|
651
|
+
await expect((crawler as any).extractLinks('https://example.com')).rejects.toThrow(
|
|
652
|
+
'Link extraction failed'
|
|
653
|
+
);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('should throw error on invalid response structure', async () => {
|
|
657
|
+
// Return a response without pages array
|
|
658
|
+
mockPythonBridge.crawl.mockResolvedValue({ invalid: 'structure' });
|
|
659
|
+
|
|
660
|
+
const crawler = new IntelligentCrawler();
|
|
661
|
+
(crawler as any).pythonBridge = mockPythonBridge;
|
|
662
|
+
|
|
663
|
+
await expect((crawler as any).extractLinks('https://example.com')).rejects.toThrow(
|
|
664
|
+
'Invalid crawl response structure'
|
|
665
|
+
);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should throw error when pages array is empty', async () => {
|
|
669
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [] });
|
|
670
|
+
|
|
671
|
+
const crawler = new IntelligentCrawler();
|
|
672
|
+
(crawler as any).pythonBridge = mockPythonBridge;
|
|
673
|
+
|
|
674
|
+
await expect((crawler as any).extractLinks('https://example.com')).rejects.toThrow(
|
|
675
|
+
'Invalid crawl response structure'
|
|
676
|
+
);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('should use headless mode for link extraction when enabled', async () => {
|
|
680
|
+
const headlessResult = {
|
|
681
|
+
html: '<html/>',
|
|
682
|
+
markdown: 'test',
|
|
683
|
+
links: ['https://example.com/page2', 'https://example.com/page3'],
|
|
684
|
+
};
|
|
685
|
+
mockPythonBridge.fetchHeadless.mockResolvedValue(headlessResult);
|
|
686
|
+
|
|
687
|
+
const crawler = new IntelligentCrawler();
|
|
688
|
+
(crawler as any).pythonBridge = mockPythonBridge;
|
|
689
|
+
|
|
690
|
+
const links = await (crawler as any).extractLinks('https://example.com', true);
|
|
691
|
+
|
|
692
|
+
expect(mockPythonBridge.fetchHeadless).toHaveBeenCalledWith('https://example.com');
|
|
693
|
+
expect(mockPythonBridge.crawl).not.toHaveBeenCalled();
|
|
694
|
+
expect(links).toEqual(['https://example.com/page2', 'https://example.com/page3']);
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('should extract href from link objects in headless mode', async () => {
|
|
698
|
+
// crawl4ai returns link objects, not strings
|
|
699
|
+
const headlessResult = {
|
|
700
|
+
html: '<html/>',
|
|
701
|
+
markdown: 'test',
|
|
702
|
+
links: [
|
|
703
|
+
{ href: 'https://example.com/page1', text: 'Page 1', title: '', base_domain: 'example.com', head_data: null, head_extraction_status: null, head_extraction_error: null, intrinsic_score: 0, contextual_score: null, total_score: null },
|
|
704
|
+
{ href: 'https://example.com/page2', text: 'Page 2', title: '', base_domain: 'example.com', head_data: null, head_extraction_status: null, head_extraction_error: null, intrinsic_score: 0, contextual_score: null, total_score: null },
|
|
705
|
+
'https://example.com/page3', // Also support plain strings
|
|
706
|
+
],
|
|
707
|
+
};
|
|
708
|
+
mockPythonBridge.fetchHeadless.mockResolvedValue(headlessResult);
|
|
709
|
+
|
|
710
|
+
const crawler = new IntelligentCrawler();
|
|
711
|
+
(crawler as any).pythonBridge = mockPythonBridge;
|
|
712
|
+
|
|
713
|
+
const links = await (crawler as any).extractLinks('https://example.com', true);
|
|
714
|
+
|
|
715
|
+
expect(links).toEqual([
|
|
716
|
+
'https://example.com/page1',
|
|
717
|
+
'https://example.com/page2',
|
|
718
|
+
'https://example.com/page3',
|
|
719
|
+
]);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it('should use regular crawl when headless is disabled', async () => {
|
|
723
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
724
|
+
pages: [{ links: ['https://example.com/page1'] }],
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
const crawler = new IntelligentCrawler();
|
|
728
|
+
(crawler as any).pythonBridge = mockPythonBridge;
|
|
729
|
+
|
|
730
|
+
const links = await (crawler as any).extractLinks('https://example.com', false);
|
|
731
|
+
|
|
732
|
+
expect(mockPythonBridge.crawl).toHaveBeenCalledWith('https://example.com');
|
|
733
|
+
expect(mockPythonBridge.fetchHeadless).not.toHaveBeenCalled();
|
|
734
|
+
expect(links).toEqual(['https://example.com/page1']);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
it('should continue crawling other pages when link extraction fails in simple mode', async () => {
|
|
738
|
+
// First page succeeds, link extraction fails, second page succeeds
|
|
739
|
+
mockClaudeClient.determineCrawlUrls.mockResolvedValue({
|
|
740
|
+
urls: ['https://example.com/page1', 'https://example.com/page2'],
|
|
741
|
+
reasoning: 'Test URLs',
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
vi.mocked(axios.get)
|
|
745
|
+
.mockResolvedValueOnce({ data: '<html><body>Seed</body></html>' }) // Seed page
|
|
746
|
+
.mockResolvedValueOnce({ data: '<html><body>Page1</body></html>' }) // First URL
|
|
747
|
+
.mockResolvedValueOnce({ data: '<html><body>Page2</body></html>' }); // Second URL
|
|
748
|
+
|
|
749
|
+
// Make link extraction fail for first URL only
|
|
750
|
+
mockPythonBridge.crawl
|
|
751
|
+
.mockRejectedValueOnce(new Error('Link extraction failed'))
|
|
752
|
+
.mockResolvedValueOnce({ pages: [{ links: [] }] });
|
|
753
|
+
|
|
754
|
+
const results = [];
|
|
755
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
756
|
+
crawlInstruction: 'Find all',
|
|
757
|
+
})) {
|
|
758
|
+
results.push(result);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Should successfully crawl both URLs despite link extraction failure
|
|
762
|
+
expect(results).toHaveLength(2);
|
|
763
|
+
expect(results[0]?.url).toBe('https://example.com/page1');
|
|
764
|
+
expect(results[1]?.url).toBe('https://example.com/page2');
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
describe('HTML to Markdown Conversion', () => {
|
|
769
|
+
it('should convert HTML to markdown for each page', async () => {
|
|
770
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
771
|
+
|
|
772
|
+
const results = [];
|
|
773
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
774
|
+
results.push(result);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
expect(vi.mocked(articleConverter.convertHtmlToMarkdown)).toHaveBeenCalledWith(
|
|
778
|
+
expect.any(String),
|
|
779
|
+
'https://example.com',
|
|
780
|
+
);
|
|
781
|
+
expect(results[0]?.markdown).toBe('# Test\n\nContent');
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
it('should include title when available', async () => {
|
|
785
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
786
|
+
vi.mocked(articleConverter.convertHtmlToMarkdown).mockResolvedValue({
|
|
787
|
+
success: true,
|
|
788
|
+
markdown: '# Test',
|
|
789
|
+
title: 'Test Page Title',
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
const results = [];
|
|
793
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
794
|
+
results.push(result);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
expect(results[0]?.title).toBe('Test Page Title');
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('should handle conversion failures', async () => {
|
|
801
|
+
mockPythonBridge.crawl.mockResolvedValue({ pages: [{ links: [] }] });
|
|
802
|
+
vi.mocked(articleConverter.convertHtmlToMarkdown).mockResolvedValue({
|
|
803
|
+
success: false,
|
|
804
|
+
markdown: '',
|
|
805
|
+
error: 'Conversion error',
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const results = [];
|
|
809
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true })) {
|
|
810
|
+
results.push(result);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Should fail to create result
|
|
814
|
+
expect(results).toHaveLength(0);
|
|
815
|
+
const errorEvents = progressEvents.filter((e) => e.type === 'error');
|
|
816
|
+
expect(errorEvents.length).toBeGreaterThan(0);
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
describe('Simple Mode Depth Control', () => {
|
|
821
|
+
it('should respect depth limit of 2 in simple mode', async () => {
|
|
822
|
+
mockPythonBridge.crawl
|
|
823
|
+
.mockResolvedValueOnce({ pages: [{ links: ['https://example.com/depth1'] }] })
|
|
824
|
+
.mockResolvedValueOnce({ pages: [{ links: ['https://example.com/depth2'] }] })
|
|
825
|
+
.mockResolvedValueOnce({ pages: [{ links: ['https://example.com/depth3'] }] });
|
|
826
|
+
|
|
827
|
+
vi.mocked(axios.get).mockResolvedValue({ data: '<html><body>Test</body></html>' });
|
|
828
|
+
|
|
829
|
+
const results = [];
|
|
830
|
+
for await (const result of crawler.crawl('https://example.com', { simple: true, maxPages: 10 })) {
|
|
831
|
+
results.push(result);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Should stop at depth 2 (seed=0, depth1=1, depth2=2)
|
|
835
|
+
expect(results.length).toBeLessThanOrEqual(3);
|
|
836
|
+
expect(results.every((r) => (r.depth ?? 0) <= 2)).toBe(true);
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
describe('Error Recovery', () => {
|
|
841
|
+
it('should continue crawling other pages after one page fails', async () => {
|
|
842
|
+
mockClaudeClient.determineCrawlUrls.mockResolvedValue({
|
|
843
|
+
urls: ['https://example.com/fail', 'https://example.com/success'],
|
|
844
|
+
reasoning: 'Test URLs',
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
vi.mocked(axios.get)
|
|
848
|
+
.mockResolvedValueOnce({ data: '<html><body>Seed page</body></html>' }) // Seed URL fetch
|
|
849
|
+
.mockRejectedValueOnce(new Error('Failed to fetch')) // First URL fails
|
|
850
|
+
.mockResolvedValueOnce({ data: '<html><body>Success</body></html>' }); // Second URL succeeds
|
|
851
|
+
|
|
852
|
+
const results = [];
|
|
853
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
854
|
+
crawlInstruction: 'Find all',
|
|
855
|
+
})) {
|
|
856
|
+
results.push(result);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Should successfully crawl the second URL
|
|
860
|
+
expect(results).toHaveLength(1);
|
|
861
|
+
expect(results[0]?.url).toBe('https://example.com/success');
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
describe('npm Package Mode (Claude CLI Not Installed)', () => {
|
|
866
|
+
it('should use simple mode when Claude CLI is not available', async () => {
|
|
867
|
+
// Simulate npm package usage without Claude Code installed
|
|
868
|
+
vi.mocked(ClaudeClient.isAvailable).mockReturnValue(false);
|
|
869
|
+
|
|
870
|
+
// Setup link extraction for simple mode
|
|
871
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
872
|
+
pages: [{ links: [] }],
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
const results: { url: string }[] = [];
|
|
876
|
+
|
|
877
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
878
|
+
crawlInstruction: 'Find all documentation pages', // Would use intelligent mode
|
|
879
|
+
maxPages: 5,
|
|
880
|
+
})) {
|
|
881
|
+
results.push(result);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Should have crawled using simple BFS mode
|
|
885
|
+
expect(results.length).toBeGreaterThan(0);
|
|
886
|
+
expect(results[0]?.url).toBe('https://example.com');
|
|
887
|
+
|
|
888
|
+
// Should have emitted progress event about mode switch
|
|
889
|
+
const modeEvent = progressEvents.find(
|
|
890
|
+
e => e.type === 'error' && e.message?.includes('Claude CLI not found')
|
|
891
|
+
);
|
|
892
|
+
expect(modeEvent).toBeDefined();
|
|
893
|
+
expect(modeEvent?.message).toContain('using simple crawl mode');
|
|
894
|
+
|
|
895
|
+
// Should NOT have called Claude's determineCrawlUrls
|
|
896
|
+
expect(mockClaudeClient.determineCrawlUrls).not.toHaveBeenCalled();
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
it('should skip extraction when Claude CLI is not available', async () => {
|
|
900
|
+
// Simulate npm package usage without Claude Code installed
|
|
901
|
+
vi.mocked(ClaudeClient.isAvailable).mockReturnValue(false);
|
|
902
|
+
|
|
903
|
+
// Setup for simple mode
|
|
904
|
+
mockPythonBridge.crawl.mockResolvedValue({
|
|
905
|
+
pages: [{ links: [] }],
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
const results: { url: string; extracted?: string }[] = [];
|
|
909
|
+
|
|
910
|
+
for await (const result of crawler.crawl('https://example.com', {
|
|
911
|
+
simple: true,
|
|
912
|
+
extractInstruction: 'Extract pricing info', // Would use Claude
|
|
913
|
+
maxPages: 1,
|
|
914
|
+
})) {
|
|
915
|
+
results.push(result);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
expect(results.length).toBe(1);
|
|
919
|
+
expect(results[0]?.extracted).toBeUndefined(); // Should not have extracted
|
|
920
|
+
|
|
921
|
+
// Should have emitted skip extraction progress event
|
|
922
|
+
const skipEvent = progressEvents.find(
|
|
923
|
+
e => e.type === 'error' && e.message?.includes('Skipping extraction')
|
|
924
|
+
);
|
|
925
|
+
expect(skipEvent).toBeDefined();
|
|
926
|
+
|
|
927
|
+
// Should NOT have called Claude's extractContent
|
|
928
|
+
expect(mockClaudeClient.extractContent).not.toHaveBeenCalled();
|
|
929
|
+
});
|
|
930
|
+
});
|
|
931
|
+
});
|