@iflow-mcp/jakeliume-webpeel 0.22.0
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/LICENSE +15 -0
- package/README.md +313 -0
- package/dist/cache.d.ts +30 -0
- package/dist/cache.js +139 -0
- package/dist/cli/commands/auth.d.ts +5 -0
- package/dist/cli/commands/auth.js +411 -0
- package/dist/cli/commands/doctor.d.ts +37 -0
- package/dist/cli/commands/doctor.js +371 -0
- package/dist/cli/commands/fetch.d.ts +6 -0
- package/dist/cli/commands/fetch.js +1345 -0
- package/dist/cli/commands/guide.d.ts +2 -0
- package/dist/cli/commands/guide.js +183 -0
- package/dist/cli/commands/interact.d.ts +5 -0
- package/dist/cli/commands/interact.js +840 -0
- package/dist/cli/commands/jobs.d.ts +5 -0
- package/dist/cli/commands/jobs.js +997 -0
- package/dist/cli/commands/monitor.d.ts +12 -0
- package/dist/cli/commands/monitor.js +197 -0
- package/dist/cli/commands/observe.d.ts +12 -0
- package/dist/cli/commands/observe.js +158 -0
- package/dist/cli/commands/screenshot.d.ts +5 -0
- package/dist/cli/commands/screenshot.js +282 -0
- package/dist/cli/commands/search.d.ts +5 -0
- package/dist/cli/commands/search.js +1021 -0
- package/dist/cli/commands/setup.d.ts +13 -0
- package/dist/cli/commands/setup.js +244 -0
- package/dist/cli/commands/skill.d.ts +15 -0
- package/dist/cli/commands/skill.js +195 -0
- package/dist/cli/utils.d.ts +84 -0
- package/dist/cli/utils.js +806 -0
- package/dist/cli-auth.d.ts +75 -0
- package/dist/cli-auth.js +369 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +99 -0
- package/dist/core/actions.d.ts +69 -0
- package/dist/core/actions.js +495 -0
- package/dist/core/agent.d.ts +98 -0
- package/dist/core/agent.js +558 -0
- package/dist/core/answer.d.ts +42 -0
- package/dist/core/answer.js +395 -0
- package/dist/core/application-tracker.d.ts +84 -0
- package/dist/core/application-tracker.js +184 -0
- package/dist/core/apply.d.ts +162 -0
- package/dist/core/apply.js +816 -0
- package/dist/core/auth-detection.d.ts +35 -0
- package/dist/core/auth-detection.js +358 -0
- package/dist/core/auto-extract.d.ts +82 -0
- package/dist/core/auto-extract.js +604 -0
- package/dist/core/auto-interact.d.ts +23 -0
- package/dist/core/auto-interact.js +246 -0
- package/dist/core/bm25-filter.d.ts +66 -0
- package/dist/core/bm25-filter.js +288 -0
- package/dist/core/branding.d.ts +54 -0
- package/dist/core/branding.js +234 -0
- package/dist/core/browser-fetch.d.ts +323 -0
- package/dist/core/browser-fetch.js +1600 -0
- package/dist/core/browser-pool.d.ts +91 -0
- package/dist/core/browser-pool.js +550 -0
- package/dist/core/budget.d.ts +42 -0
- package/dist/core/budget.js +324 -0
- package/dist/core/business-intel.d.ts +47 -0
- package/dist/core/business-intel.js +279 -0
- package/dist/core/cache.d.ts +13 -0
- package/dist/core/cache.js +121 -0
- package/dist/core/cf-worker-proxy.d.ts +32 -0
- package/dist/core/cf-worker-proxy.js +87 -0
- package/dist/core/challenge-detection.d.ts +26 -0
- package/dist/core/challenge-detection.js +468 -0
- package/dist/core/change-tracking.d.ts +75 -0
- package/dist/core/change-tracking.js +276 -0
- package/dist/core/chunker.d.ts +46 -0
- package/dist/core/chunker.js +249 -0
- package/dist/core/chunking.d.ts +42 -0
- package/dist/core/chunking.js +181 -0
- package/dist/core/circuit-breaker.d.ts +44 -0
- package/dist/core/circuit-breaker.js +85 -0
- package/dist/core/content-pruner.d.ts +47 -0
- package/dist/core/content-pruner.js +425 -0
- package/dist/core/cookie-cache.d.ts +60 -0
- package/dist/core/cookie-cache.js +163 -0
- package/dist/core/crawl-checkpoint.d.ts +54 -0
- package/dist/core/crawl-checkpoint.js +104 -0
- package/dist/core/crawler.d.ts +84 -0
- package/dist/core/crawler.js +349 -0
- package/dist/core/cross-verify.d.ts +27 -0
- package/dist/core/cross-verify.js +93 -0
- package/dist/core/deep-fetch.d.ts +74 -0
- package/dist/core/deep-fetch.js +405 -0
- package/dist/core/deep-research.d.ts +141 -0
- package/dist/core/deep-research.js +972 -0
- package/dist/core/design-analysis.d.ts +70 -0
- package/dist/core/design-analysis.js +490 -0
- package/dist/core/design-compare.d.ts +38 -0
- package/dist/core/design-compare.js +264 -0
- package/dist/core/diff.d.ts +61 -0
- package/dist/core/diff.js +289 -0
- package/dist/core/dns-cache.d.ts +20 -0
- package/dist/core/dns-cache.js +198 -0
- package/dist/core/documents.d.ts +23 -0
- package/dist/core/documents.js +123 -0
- package/dist/core/domain-memory.d.ts +66 -0
- package/dist/core/domain-memory.js +163 -0
- package/dist/core/domain-verify.d.ts +40 -0
- package/dist/core/domain-verify.js +379 -0
- package/dist/core/engine-ranker.d.ts +112 -0
- package/dist/core/engine-ranker.js +395 -0
- package/dist/core/extract-inline.d.ts +38 -0
- package/dist/core/extract-inline.js +215 -0
- package/dist/core/extract-listings.d.ts +38 -0
- package/dist/core/extract-listings.js +461 -0
- package/dist/core/extract.d.ts +9 -0
- package/dist/core/extract.js +139 -0
- package/dist/core/fetch-cache.d.ts +57 -0
- package/dist/core/fetch-cache.js +95 -0
- package/dist/core/fetcher.d.ts +13 -0
- package/dist/core/fetcher.js +12 -0
- package/dist/core/google-cache.d.ts +29 -0
- package/dist/core/google-cache.js +180 -0
- package/dist/core/google-serp-parser.d.ts +82 -0
- package/dist/core/google-serp-parser.js +287 -0
- package/dist/core/hotel-search.d.ts +122 -0
- package/dist/core/hotel-search.js +382 -0
- package/dist/core/http-fetch.d.ts +72 -0
- package/dist/core/http-fetch.js +820 -0
- package/dist/core/human.d.ts +175 -0
- package/dist/core/human.js +680 -0
- package/dist/core/image-caption.d.ts +44 -0
- package/dist/core/image-caption.js +271 -0
- package/dist/core/jobs.d.ts +75 -0
- package/dist/core/jobs.js +634 -0
- package/dist/core/json-ld.d.ts +15 -0
- package/dist/core/json-ld.js +617 -0
- package/dist/core/language-detect.d.ts +18 -0
- package/dist/core/language-detect.js +135 -0
- package/dist/core/links.d.ts +10 -0
- package/dist/core/links.js +44 -0
- package/dist/core/llm-extract.d.ts +71 -0
- package/dist/core/llm-extract.js +507 -0
- package/dist/core/llm-provider.d.ts +100 -0
- package/dist/core/llm-provider.js +702 -0
- package/dist/core/local-search.d.ts +60 -0
- package/dist/core/local-search.js +308 -0
- package/dist/core/logger.d.ts +28 -0
- package/dist/core/logger.js +104 -0
- package/dist/core/map.d.ts +33 -0
- package/dist/core/map.js +127 -0
- package/dist/core/markdown.d.ts +92 -0
- package/dist/core/markdown.js +809 -0
- package/dist/core/metadata.d.ts +34 -0
- package/dist/core/metadata.js +422 -0
- package/dist/core/observe.d.ts +113 -0
- package/dist/core/observe.js +395 -0
- package/dist/core/ocr.d.ts +12 -0
- package/dist/core/ocr.js +33 -0
- package/dist/core/paginate.d.ts +31 -0
- package/dist/core/paginate.js +106 -0
- package/dist/core/pdf.d.ts +8 -0
- package/dist/core/pdf.js +25 -0
- package/dist/core/peel-tls.d.ts +25 -0
- package/dist/core/peel-tls.js +220 -0
- package/dist/core/pipeline.d.ts +132 -0
- package/dist/core/pipeline.js +1666 -0
- package/dist/core/profiles.d.ts +61 -0
- package/dist/core/profiles.js +350 -0
- package/dist/core/prompt-guard.d.ts +30 -0
- package/dist/core/prompt-guard.js +119 -0
- package/dist/core/proxy-config.d.ts +90 -0
- package/dist/core/proxy-config.js +172 -0
- package/dist/core/quick-answer.d.ts +53 -0
- package/dist/core/quick-answer.js +833 -0
- package/dist/core/rate-governor.d.ts +80 -0
- package/dist/core/rate-governor.js +238 -0
- package/dist/core/readability.d.ts +57 -0
- package/dist/core/readability.js +533 -0
- package/dist/core/research.d.ts +66 -0
- package/dist/core/research.js +270 -0
- package/dist/core/retry.d.ts +60 -0
- package/dist/core/retry.js +119 -0
- package/dist/core/safe-browsing.d.ts +30 -0
- package/dist/core/safe-browsing.js +206 -0
- package/dist/core/schema-extraction.d.ts +66 -0
- package/dist/core/schema-extraction.js +352 -0
- package/dist/core/schema-postprocess.d.ts +32 -0
- package/dist/core/schema-postprocess.js +469 -0
- package/dist/core/schema-templates.d.ts +19 -0
- package/dist/core/schema-templates.js +143 -0
- package/dist/core/screenshot.d.ts +224 -0
- package/dist/core/screenshot.js +207 -0
- package/dist/core/search-engines.d.ts +25 -0
- package/dist/core/search-engines.js +182 -0
- package/dist/core/search-provider.d.ts +243 -0
- package/dist/core/search-provider.js +1629 -0
- package/dist/core/searxng-provider.d.ts +35 -0
- package/dist/core/searxng-provider.js +105 -0
- package/dist/core/selective-evidence.d.ts +151 -0
- package/dist/core/selective-evidence.js +389 -0
- package/dist/core/site-search.d.ts +44 -0
- package/dist/core/site-search.js +252 -0
- package/dist/core/sitemap.d.ts +23 -0
- package/dist/core/sitemap.js +105 -0
- package/dist/core/source-credibility.d.ts +29 -0
- package/dist/core/source-credibility.js +584 -0
- package/dist/core/source-scoring.d.ts +166 -0
- package/dist/core/source-scoring.js +396 -0
- package/dist/core/stemmer.d.ts +38 -0
- package/dist/core/stemmer.js +509 -0
- package/dist/core/strategies.d.ts +104 -0
- package/dist/core/strategies.js +1044 -0
- package/dist/core/strategy-hooks.d.ts +145 -0
- package/dist/core/strategy-hooks.js +74 -0
- package/dist/core/structured-extract.d.ts +43 -0
- package/dist/core/structured-extract.js +550 -0
- package/dist/core/summarize.d.ts +17 -0
- package/dist/core/summarize.js +78 -0
- package/dist/core/synonyms.d.ts +42 -0
- package/dist/core/synonyms.js +184 -0
- package/dist/core/system-monitor.d.ts +61 -0
- package/dist/core/system-monitor.js +133 -0
- package/dist/core/table-format.d.ts +30 -0
- package/dist/core/table-format.js +146 -0
- package/dist/core/threat-feeds.d.ts +23 -0
- package/dist/core/threat-feeds.js +104 -0
- package/dist/core/timing.d.ts +21 -0
- package/dist/core/timing.js +33 -0
- package/dist/core/transcript-export.d.ts +47 -0
- package/dist/core/transcript-export.js +107 -0
- package/dist/core/user-agents.d.ts +82 -0
- package/dist/core/user-agents.js +239 -0
- package/dist/core/vertical-search.d.ts +54 -0
- package/dist/core/vertical-search.js +158 -0
- package/dist/core/watch-manager.d.ts +175 -0
- package/dist/core/watch-manager.js +416 -0
- package/dist/core/watch.d.ts +101 -0
- package/dist/core/watch.js +389 -0
- package/dist/core/youtube.d.ts +130 -0
- package/dist/core/youtube.js +1175 -0
- package/dist/ee/challenge-re-export.d.ts +1 -0
- package/dist/ee/challenge-re-export.js +1 -0
- package/dist/ee/challenge-solver.d.ts +72 -0
- package/dist/ee/challenge-solver.js +720 -0
- package/dist/ee/domain-extractors.d.ts +8 -0
- package/dist/ee/domain-extractors.js +8 -0
- package/dist/ee/domain-intel.d.ts +16 -0
- package/dist/ee/domain-intel.js +133 -0
- package/dist/ee/extractors/allrecipes.d.ts +2 -0
- package/dist/ee/extractors/allrecipes.js +120 -0
- package/dist/ee/extractors/amazon.d.ts +2 -0
- package/dist/ee/extractors/amazon.js +78 -0
- package/dist/ee/extractors/arxiv.d.ts +2 -0
- package/dist/ee/extractors/arxiv.js +137 -0
- package/dist/ee/extractors/bestbuy.d.ts +2 -0
- package/dist/ee/extractors/bestbuy.js +78 -0
- package/dist/ee/extractors/carscom.d.ts +2 -0
- package/dist/ee/extractors/carscom.js +121 -0
- package/dist/ee/extractors/coingecko.d.ts +2 -0
- package/dist/ee/extractors/coingecko.js +134 -0
- package/dist/ee/extractors/craigslist.d.ts +2 -0
- package/dist/ee/extractors/craigslist.js +92 -0
- package/dist/ee/extractors/devto.d.ts +2 -0
- package/dist/ee/extractors/devto.js +135 -0
- package/dist/ee/extractors/ebay.d.ts +2 -0
- package/dist/ee/extractors/ebay.js +90 -0
- package/dist/ee/extractors/espn.d.ts +2 -0
- package/dist/ee/extractors/espn.js +260 -0
- package/dist/ee/extractors/etsy.d.ts +2 -0
- package/dist/ee/extractors/etsy.js +52 -0
- package/dist/ee/extractors/facebook.d.ts +2 -0
- package/dist/ee/extractors/facebook.js +46 -0
- package/dist/ee/extractors/github.d.ts +2 -0
- package/dist/ee/extractors/github.js +196 -0
- package/dist/ee/extractors/google-flights.d.ts +2 -0
- package/dist/ee/extractors/google-flights.js +176 -0
- package/dist/ee/extractors/hackernews.d.ts +2 -0
- package/dist/ee/extractors/hackernews.js +147 -0
- package/dist/ee/extractors/imdb.d.ts +2 -0
- package/dist/ee/extractors/imdb.js +172 -0
- package/dist/ee/extractors/index.d.ts +26 -0
- package/dist/ee/extractors/index.js +247 -0
- package/dist/ee/extractors/instagram.d.ts +2 -0
- package/dist/ee/extractors/instagram.js +102 -0
- package/dist/ee/extractors/kalshi.d.ts +2 -0
- package/dist/ee/extractors/kalshi.js +121 -0
- package/dist/ee/extractors/kayak-cars.d.ts +2 -0
- package/dist/ee/extractors/kayak-cars.js +270 -0
- package/dist/ee/extractors/linkedin.d.ts +2 -0
- package/dist/ee/extractors/linkedin.js +113 -0
- package/dist/ee/extractors/medium.d.ts +2 -0
- package/dist/ee/extractors/medium.js +130 -0
- package/dist/ee/extractors/news.d.ts +4 -0
- package/dist/ee/extractors/news.js +173 -0
- package/dist/ee/extractors/npm.d.ts +2 -0
- package/dist/ee/extractors/npm.js +86 -0
- package/dist/ee/extractors/pdf.d.ts +2 -0
- package/dist/ee/extractors/pdf.js +108 -0
- package/dist/ee/extractors/pinterest.d.ts +2 -0
- package/dist/ee/extractors/pinterest.js +34 -0
- package/dist/ee/extractors/polymarket.d.ts +2 -0
- package/dist/ee/extractors/polymarket.js +358 -0
- package/dist/ee/extractors/producthunt.d.ts +2 -0
- package/dist/ee/extractors/producthunt.js +88 -0
- package/dist/ee/extractors/pubmed.d.ts +2 -0
- package/dist/ee/extractors/pubmed.js +162 -0
- package/dist/ee/extractors/pypi.d.ts +2 -0
- package/dist/ee/extractors/pypi.js +80 -0
- package/dist/ee/extractors/reddit.d.ts +2 -0
- package/dist/ee/extractors/reddit.js +438 -0
- package/dist/ee/extractors/redfin.d.ts +2 -0
- package/dist/ee/extractors/redfin.js +156 -0
- package/dist/ee/extractors/semanticscholar.d.ts +2 -0
- package/dist/ee/extractors/semanticscholar.js +131 -0
- package/dist/ee/extractors/shared.d.ts +12 -0
- package/dist/ee/extractors/shared.js +76 -0
- package/dist/ee/extractors/soundcloud.d.ts +2 -0
- package/dist/ee/extractors/soundcloud.js +34 -0
- package/dist/ee/extractors/sportsbetting.d.ts +2 -0
- package/dist/ee/extractors/sportsbetting.js +37 -0
- package/dist/ee/extractors/spotify.d.ts +2 -0
- package/dist/ee/extractors/spotify.js +34 -0
- package/dist/ee/extractors/stackoverflow.d.ts +2 -0
- package/dist/ee/extractors/stackoverflow.js +61 -0
- package/dist/ee/extractors/substack.d.ts +2 -0
- package/dist/ee/extractors/substack.js +115 -0
- package/dist/ee/extractors/substackroot.d.ts +2 -0
- package/dist/ee/extractors/substackroot.js +46 -0
- package/dist/ee/extractors/tiktok.d.ts +2 -0
- package/dist/ee/extractors/tiktok.js +29 -0
- package/dist/ee/extractors/tradingview.d.ts +2 -0
- package/dist/ee/extractors/tradingview.js +182 -0
- package/dist/ee/extractors/twitch.d.ts +2 -0
- package/dist/ee/extractors/twitch.js +36 -0
- package/dist/ee/extractors/twitter.d.ts +2 -0
- package/dist/ee/extractors/twitter.js +327 -0
- package/dist/ee/extractors/types.d.ts +14 -0
- package/dist/ee/extractors/types.js +1 -0
- package/dist/ee/extractors/walmart.d.ts +2 -0
- package/dist/ee/extractors/walmart.js +50 -0
- package/dist/ee/extractors/weather.d.ts +2 -0
- package/dist/ee/extractors/weather.js +133 -0
- package/dist/ee/extractors/wikipedia.d.ts +4 -0
- package/dist/ee/extractors/wikipedia.js +235 -0
- package/dist/ee/extractors/yelp.d.ts +2 -0
- package/dist/ee/extractors/yelp.js +216 -0
- package/dist/ee/extractors/youtube.d.ts +2 -0
- package/dist/ee/extractors/youtube.js +189 -0
- package/dist/ee/extractors/zillow.d.ts +54 -0
- package/dist/ee/extractors/zillow.js +247 -0
- package/dist/ee/extractors-re-export.d.ts +1 -0
- package/dist/ee/extractors-re-export.js +1 -0
- package/dist/ee/premium-hooks.d.ts +20 -0
- package/dist/ee/premium-hooks.js +50 -0
- package/dist/ee/spa-detection.d.ts +2 -0
- package/dist/ee/spa-detection.js +2 -0
- package/dist/ee/stability.d.ts +4 -0
- package/dist/ee/stability.js +29 -0
- package/dist/ee/swr-cache.d.ts +14 -0
- package/dist/ee/swr-cache.js +34 -0
- package/dist/index.d.ts +143 -0
- package/dist/index.js +291 -0
- package/dist/integrations/index.d.ts +2 -0
- package/dist/integrations/index.js +2 -0
- package/dist/integrations/langchain.d.ts +64 -0
- package/dist/integrations/langchain.js +115 -0
- package/dist/integrations/llamaindex.d.ts +50 -0
- package/dist/integrations/llamaindex.js +91 -0
- package/dist/mcp/handlers/act.d.ts +5 -0
- package/dist/mcp/handlers/act.js +34 -0
- package/dist/mcp/handlers/definitions.d.ts +6 -0
- package/dist/mcp/handlers/definitions.js +395 -0
- package/dist/mcp/handlers/extract.d.ts +7 -0
- package/dist/mcp/handlers/extract.js +135 -0
- package/dist/mcp/handlers/fetch.d.ts +6 -0
- package/dist/mcp/handlers/fetch.js +98 -0
- package/dist/mcp/handlers/find.d.ts +5 -0
- package/dist/mcp/handlers/find.js +137 -0
- package/dist/mcp/handlers/index.d.ts +13 -0
- package/dist/mcp/handlers/index.js +63 -0
- package/dist/mcp/handlers/legacy.d.ts +25 -0
- package/dist/mcp/handlers/legacy.js +450 -0
- package/dist/mcp/handlers/meta.d.ts +6 -0
- package/dist/mcp/handlers/meta.js +40 -0
- package/dist/mcp/handlers/monitor.d.ts +5 -0
- package/dist/mcp/handlers/monitor.js +41 -0
- package/dist/mcp/handlers/observe.d.ts +8 -0
- package/dist/mcp/handlers/observe.js +37 -0
- package/dist/mcp/handlers/read.d.ts +6 -0
- package/dist/mcp/handlers/read.js +78 -0
- package/dist/mcp/handlers/see.d.ts +5 -0
- package/dist/mcp/handlers/see.js +75 -0
- package/dist/mcp/handlers/types.d.ts +29 -0
- package/dist/mcp/handlers/types.js +28 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +108 -0
- package/dist/mcp/smart-router.d.ts +23 -0
- package/dist/mcp/smart-router.js +178 -0
- package/dist/server/app.d.ts +14 -0
- package/dist/server/app.js +632 -0
- package/dist/server/auth-store.d.ts +28 -0
- package/dist/server/auth-store.js +88 -0
- package/dist/server/bull-queues.d.ts +60 -0
- package/dist/server/bull-queues.js +90 -0
- package/dist/server/email-service.d.ts +55 -0
- package/dist/server/email-service.js +291 -0
- package/dist/server/job-queue.d.ts +100 -0
- package/dist/server/job-queue.js +145 -0
- package/dist/server/logger.d.ts +10 -0
- package/dist/server/logger.js +37 -0
- package/dist/server/middleware/audit-log.d.ts +14 -0
- package/dist/server/middleware/audit-log.js +73 -0
- package/dist/server/middleware/auth.d.ts +35 -0
- package/dist/server/middleware/auth.js +225 -0
- package/dist/server/middleware/rate-limit.d.ts +50 -0
- package/dist/server/middleware/rate-limit.js +270 -0
- package/dist/server/middleware/scope-guard.d.ts +25 -0
- package/dist/server/middleware/scope-guard.js +45 -0
- package/dist/server/middleware/url-validator.d.ts +15 -0
- package/dist/server/middleware/url-validator.js +201 -0
- package/dist/server/openapi.yaml +6418 -0
- package/dist/server/pg-auth-store.d.ts +146 -0
- package/dist/server/pg-auth-store.js +576 -0
- package/dist/server/pg-job-queue.d.ts +59 -0
- package/dist/server/pg-job-queue.js +375 -0
- package/dist/server/routes/activity.d.ts +6 -0
- package/dist/server/routes/activity.js +79 -0
- package/dist/server/routes/admin-active.d.ts +7 -0
- package/dist/server/routes/admin-active.js +120 -0
- package/dist/server/routes/admin-stats.d.ts +7 -0
- package/dist/server/routes/admin-stats.js +176 -0
- package/dist/server/routes/agent.d.ts +24 -0
- package/dist/server/routes/agent.js +480 -0
- package/dist/server/routes/answer.d.ts +5 -0
- package/dist/server/routes/answer.js +125 -0
- package/dist/server/routes/ask.d.ts +28 -0
- package/dist/server/routes/ask.js +295 -0
- package/dist/server/routes/batch.d.ts +6 -0
- package/dist/server/routes/batch.js +493 -0
- package/dist/server/routes/cache-warm.d.ts +25 -0
- package/dist/server/routes/cache-warm.js +212 -0
- package/dist/server/routes/cli-usage.d.ts +6 -0
- package/dist/server/routes/cli-usage.js +127 -0
- package/dist/server/routes/compat.d.ts +23 -0
- package/dist/server/routes/compat.js +652 -0
- package/dist/server/routes/crawl.d.ts +13 -0
- package/dist/server/routes/crawl.js +287 -0
- package/dist/server/routes/deep-fetch.d.ts +8 -0
- package/dist/server/routes/deep-fetch.js +57 -0
- package/dist/server/routes/deep-research.d.ts +11 -0
- package/dist/server/routes/deep-research.js +232 -0
- package/dist/server/routes/demo.d.ts +24 -0
- package/dist/server/routes/demo.js +517 -0
- package/dist/server/routes/do.d.ts +8 -0
- package/dist/server/routes/do.js +72 -0
- package/dist/server/routes/extract.d.ts +14 -0
- package/dist/server/routes/extract.js +325 -0
- package/dist/server/routes/feed.d.ts +15 -0
- package/dist/server/routes/feed.js +311 -0
- package/dist/server/routes/fetch-queue.d.ts +13 -0
- package/dist/server/routes/fetch-queue.js +357 -0
- package/dist/server/routes/fetch.d.ts +7 -0
- package/dist/server/routes/fetch.js +1274 -0
- package/dist/server/routes/go.d.ts +14 -0
- package/dist/server/routes/go.js +81 -0
- package/dist/server/routes/health.d.ts +11 -0
- package/dist/server/routes/health.js +141 -0
- package/dist/server/routes/jobs.d.ts +7 -0
- package/dist/server/routes/jobs.js +574 -0
- package/dist/server/routes/map.d.ts +11 -0
- package/dist/server/routes/map.js +116 -0
- package/dist/server/routes/mcp.d.ts +14 -0
- package/dist/server/routes/mcp.js +197 -0
- package/dist/server/routes/metrics.d.ts +37 -0
- package/dist/server/routes/metrics.js +149 -0
- package/dist/server/routes/oauth.d.ts +9 -0
- package/dist/server/routes/oauth.js +396 -0
- package/dist/server/routes/playground.d.ts +17 -0
- package/dist/server/routes/playground.js +283 -0
- package/dist/server/routes/reader.d.ts +18 -0
- package/dist/server/routes/reader.js +192 -0
- package/dist/server/routes/research.d.ts +14 -0
- package/dist/server/routes/research.js +482 -0
- package/dist/server/routes/screenshot.d.ts +22 -0
- package/dist/server/routes/screenshot.js +820 -0
- package/dist/server/routes/search.d.ts +6 -0
- package/dist/server/routes/search.js +874 -0
- package/dist/server/routes/session.d.ts +17 -0
- package/dist/server/routes/session.js +548 -0
- package/dist/server/routes/share.d.ts +18 -0
- package/dist/server/routes/share.js +462 -0
- package/dist/server/routes/smart-search/handlers/cars.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/cars.js +102 -0
- package/dist/server/routes/smart-search/handlers/flights.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/flights.js +72 -0
- package/dist/server/routes/smart-search/handlers/general.d.ts +13 -0
- package/dist/server/routes/smart-search/handlers/general.js +717 -0
- package/dist/server/routes/smart-search/handlers/hotels.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/hotels.js +88 -0
- package/dist/server/routes/smart-search/handlers/products.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/products.js +1309 -0
- package/dist/server/routes/smart-search/handlers/rental.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/rental.js +154 -0
- package/dist/server/routes/smart-search/handlers/restaurants.d.ts +2 -0
- package/dist/server/routes/smart-search/handlers/restaurants.js +225 -0
- package/dist/server/routes/smart-search/handlers/transit-verdict.d.ts +41 -0
- package/dist/server/routes/smart-search/handlers/transit-verdict.js +224 -0
- package/dist/server/routes/smart-search/index.d.ts +19 -0
- package/dist/server/routes/smart-search/index.js +546 -0
- package/dist/server/routes/smart-search/intent.d.ts +3 -0
- package/dist/server/routes/smart-search/intent.js +264 -0
- package/dist/server/routes/smart-search/llm.d.ts +16 -0
- package/dist/server/routes/smart-search/llm.js +70 -0
- package/dist/server/routes/smart-search/sources/reddit.d.ts +18 -0
- package/dist/server/routes/smart-search/sources/reddit.js +34 -0
- package/dist/server/routes/smart-search/sources/yelp.d.ts +25 -0
- package/dist/server/routes/smart-search/sources/yelp.js +171 -0
- package/dist/server/routes/smart-search/sources/youtube.d.ts +8 -0
- package/dist/server/routes/smart-search/sources/youtube.js +9 -0
- package/dist/server/routes/smart-search/types.d.ts +81 -0
- package/dist/server/routes/smart-search/types.js +1 -0
- package/dist/server/routes/smart-search/utils.d.ts +20 -0
- package/dist/server/routes/smart-search/utils.js +146 -0
- package/dist/server/routes/stats.d.ts +6 -0
- package/dist/server/routes/stats.js +71 -0
- package/dist/server/routes/stripe.d.ts +15 -0
- package/dist/server/routes/stripe.js +296 -0
- package/dist/server/routes/transcript-export.d.ts +10 -0
- package/dist/server/routes/transcript-export.js +178 -0
- package/dist/server/routes/usage.d.ts +9 -0
- package/dist/server/routes/usage.js +279 -0
- package/dist/server/routes/users.d.ts +8 -0
- package/dist/server/routes/users.js +1867 -0
- package/dist/server/routes/watch.d.ts +15 -0
- package/dist/server/routes/watch.js +309 -0
- package/dist/server/routes/webhooks.d.ts +26 -0
- package/dist/server/routes/webhooks.js +170 -0
- package/dist/server/routes/youtube.d.ts +6 -0
- package/dist/server/routes/youtube.js +130 -0
- package/dist/server/sentry.d.ts +14 -0
- package/dist/server/sentry.js +104 -0
- package/dist/server/types.d.ts +15 -0
- package/dist/server/types.js +7 -0
- package/dist/server/utils/response.d.ts +44 -0
- package/dist/server/utils/response.js +69 -0
- package/dist/server/utils/sse.d.ts +22 -0
- package/dist/server/utils/sse.js +38 -0
- package/dist/types.d.ts +552 -0
- package/dist/types.js +39 -0
- package/llms.txt +105 -0
- package/package.json +189 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hotel search module — searches multiple travel sites and returns sorted hotel listings.
|
|
3
|
+
*
|
|
4
|
+
* Sources: Kayak, Booking.com, Google Travel
|
|
5
|
+
* All sources are fetched in parallel; failures are captured per-source without
|
|
6
|
+
* crashing the overall search.
|
|
7
|
+
*/
|
|
8
|
+
import { peel } from '../index.js';
|
|
9
|
+
import { extractListings } from './extract-listings.js';
|
|
10
|
+
import { findSchemaForUrl, extractWithSchema } from './schema-extraction.js';
|
|
11
|
+
// ── Date Parsing ──────────────────────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Parse a date string (ISO or relative) into an ISO date string (YYYY-MM-DD).
|
|
14
|
+
*
|
|
15
|
+
* Supported relative formats:
|
|
16
|
+
* - "tomorrow" → today + 1 day
|
|
17
|
+
* - "next <weekday>" → next occurrence of that weekday
|
|
18
|
+
* - ISO date "2026-02-20" → returned as-is
|
|
19
|
+
*/
|
|
20
|
+
export function parseDate(input, baseDate) {
|
|
21
|
+
const base = baseDate ?? new Date();
|
|
22
|
+
// Normalise
|
|
23
|
+
const normalised = input.trim().toLowerCase();
|
|
24
|
+
if (normalised === 'today') {
|
|
25
|
+
return toIsoDate(base);
|
|
26
|
+
}
|
|
27
|
+
if (normalised === 'tomorrow') {
|
|
28
|
+
const d = new Date(base);
|
|
29
|
+
d.setDate(d.getDate() + 1);
|
|
30
|
+
return toIsoDate(d);
|
|
31
|
+
}
|
|
32
|
+
// "next <weekday>"
|
|
33
|
+
const nextMatch = normalised.match(/^next\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)$/);
|
|
34
|
+
if (nextMatch) {
|
|
35
|
+
const weekdays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
|
36
|
+
const targetDay = weekdays.indexOf(nextMatch[1]);
|
|
37
|
+
const d = new Date(base);
|
|
38
|
+
const currentDay = d.getDay();
|
|
39
|
+
let daysUntil = targetDay - currentDay;
|
|
40
|
+
if (daysUntil <= 0)
|
|
41
|
+
daysUntil += 7;
|
|
42
|
+
d.setDate(d.getDate() + daysUntil);
|
|
43
|
+
return toIsoDate(d);
|
|
44
|
+
}
|
|
45
|
+
// Try ISO date (YYYY-MM-DD)
|
|
46
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(input.trim())) {
|
|
47
|
+
return input.trim();
|
|
48
|
+
}
|
|
49
|
+
// Fallback: try to parse as a generic date string
|
|
50
|
+
const parsed = new Date(input);
|
|
51
|
+
if (!isNaN(parsed.getTime())) {
|
|
52
|
+
return toIsoDate(parsed);
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Unrecognized date format: "${input}"`);
|
|
55
|
+
}
|
|
56
|
+
function toIsoDate(d) {
|
|
57
|
+
const yyyy = d.getFullYear();
|
|
58
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
59
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
60
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
61
|
+
}
|
|
62
|
+
/** Add N days to an ISO date string and return the new ISO date string. */
|
|
63
|
+
export function addDays(isoDate, days) {
|
|
64
|
+
const d = new Date(isoDate + 'T12:00:00Z');
|
|
65
|
+
d.setUTCDate(d.getUTCDate() + days);
|
|
66
|
+
const yyyy = d.getUTCFullYear();
|
|
67
|
+
const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
68
|
+
const dd = String(d.getUTCDate()).padStart(2, '0');
|
|
69
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
70
|
+
}
|
|
71
|
+
// ── URL Builders ──────────────────────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* Convert a destination name to a Kayak-friendly slug.
|
|
74
|
+
* e.g. "Manhattan, New York" → "Manhattan,New-York"
|
|
75
|
+
* e.g. "Long Island City" → "Long-Island-City"
|
|
76
|
+
*/
|
|
77
|
+
export function toKayakSlug(destination) {
|
|
78
|
+
return destination
|
|
79
|
+
.split(',')
|
|
80
|
+
.map(part => part.trim().replace(/\s+/g, '-'))
|
|
81
|
+
.join(',');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build the search URL for each source.
|
|
85
|
+
*/
|
|
86
|
+
export function buildSourceUrls(destination, checkin, checkout) {
|
|
87
|
+
const kayakSlug = toKayakSlug(destination);
|
|
88
|
+
const bookingDest = encodeURIComponent(destination);
|
|
89
|
+
const googleDest = destination.replace(/\s+/g, '+');
|
|
90
|
+
const expediaDest = encodeURIComponent(destination);
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
name: 'kayak',
|
|
94
|
+
url: `https://www.kayak.com/hotels/${kayakSlug}/${checkin}/${checkout}?sort=price_a`,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'booking',
|
|
98
|
+
url: `https://www.booking.com/searchresults.html?ss=${bookingDest}&checkin=${checkin}&checkout=${checkout}&order=price`,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'google',
|
|
102
|
+
url: `https://www.google.com/travel/hotels/${googleDest}`,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'expedia',
|
|
106
|
+
url: `https://www.expedia.com/Hotel-Search?destination=${expediaDest}&startDate=${checkin}&endDate=${checkout}&sort=PRICE_LOW_TO_HIGH`,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
// ── Price & Rating Parsers ────────────────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Parse a price display string into a numeric USD value.
|
|
113
|
+
* Returns null if unparseable.
|
|
114
|
+
*
|
|
115
|
+
* Examples:
|
|
116
|
+
* "$119" → 119
|
|
117
|
+
* "$1,299" → 1299
|
|
118
|
+
* "£85" → 85 (GBP treated as USD approximation)
|
|
119
|
+
* "€95" → 95
|
|
120
|
+
* "US$200" → 200
|
|
121
|
+
*/
|
|
122
|
+
export function parsePrice(raw) {
|
|
123
|
+
if (!raw)
|
|
124
|
+
return null;
|
|
125
|
+
// Remove currency symbols and "US$" prefix, commas, whitespace
|
|
126
|
+
const cleaned = raw.replace(/US\$|[$£€¥₹]/g, '').replace(/,/g, '').trim();
|
|
127
|
+
// Extract first number
|
|
128
|
+
const match = cleaned.match(/(\d+(?:\.\d+)?)/);
|
|
129
|
+
if (!match)
|
|
130
|
+
return null;
|
|
131
|
+
const n = parseFloat(match[1]);
|
|
132
|
+
return isNaN(n) ? null : n;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Parse a rating string into a numeric value.
|
|
136
|
+
* Returns null if unparseable.
|
|
137
|
+
*
|
|
138
|
+
* Examples:
|
|
139
|
+
* "Scored 8.4" → 8.4
|
|
140
|
+
* "4.2/5" → 4.2
|
|
141
|
+
* "4.2/5 (1.4K)" → 4.2
|
|
142
|
+
* "8.3" → 8.3
|
|
143
|
+
* "Very Good 8.6" → 8.6
|
|
144
|
+
*/
|
|
145
|
+
export function parseRating(raw) {
|
|
146
|
+
if (!raw)
|
|
147
|
+
return null;
|
|
148
|
+
// "Scored N.N" or "Very Good N.N" etc.
|
|
149
|
+
const scoredMatch = raw.match(/(\d+(?:\.\d+)?)\s*\/\s*\d/);
|
|
150
|
+
if (scoredMatch) {
|
|
151
|
+
const n = parseFloat(scoredMatch[1]);
|
|
152
|
+
return isNaN(n) ? null : n;
|
|
153
|
+
}
|
|
154
|
+
// Extract last number (handles "Scored 8.4", "Very Good 8.6", standalone "8.3")
|
|
155
|
+
const numMatch = raw.match(/(\d+(?:\.\d+)?)/g);
|
|
156
|
+
if (!numMatch)
|
|
157
|
+
return null;
|
|
158
|
+
// Take the last number that looks like a rating (0–10 scale or 0–5 scale)
|
|
159
|
+
for (let i = numMatch.length - 1; i >= 0; i--) {
|
|
160
|
+
const n = parseFloat(numMatch[i]);
|
|
161
|
+
if (!isNaN(n) && n >= 0 && n <= 10)
|
|
162
|
+
return n;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
// ── Result Normalisation ──────────────────────────────────────────────────────
|
|
167
|
+
/**
|
|
168
|
+
* Map an extracted listing item to a HotelResult, tagged with the source name.
|
|
169
|
+
*/
|
|
170
|
+
function normaliseToHotelResult(item, sourceName) {
|
|
171
|
+
const name = item.title?.trim();
|
|
172
|
+
if (!name)
|
|
173
|
+
return null;
|
|
174
|
+
const priceDisplay = item.price ?? '';
|
|
175
|
+
const ratingDisplay = item.rating ?? '';
|
|
176
|
+
return {
|
|
177
|
+
name,
|
|
178
|
+
price: parsePrice(priceDisplay),
|
|
179
|
+
priceDisplay,
|
|
180
|
+
rating: parseRating(ratingDisplay),
|
|
181
|
+
ratingDisplay,
|
|
182
|
+
source: sourceName,
|
|
183
|
+
link: item.link ?? '',
|
|
184
|
+
location: item.description?.trim() || undefined,
|
|
185
|
+
image: item.image || undefined,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// ── Deduplication ─────────────────────────────────────────────────────────────
|
|
189
|
+
/**
|
|
190
|
+
* Deduplicate hotel results by name (case-insensitive).
|
|
191
|
+
* When duplicates exist, keep the one with the most data (price + rating),
|
|
192
|
+
* with lowest price as a tiebreaker.
|
|
193
|
+
*/
|
|
194
|
+
export function deduplicateHotels(hotels) {
|
|
195
|
+
const byName = new Map();
|
|
196
|
+
for (const hotel of hotels) {
|
|
197
|
+
const key = hotel.name.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
198
|
+
const existing = byName.get(key);
|
|
199
|
+
if (!existing) {
|
|
200
|
+
byName.set(key, hotel);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
// Score = number of non-null data fields
|
|
204
|
+
const scoreNew = (hotel.price !== null ? 1 : 0) + (hotel.rating !== null ? 1 : 0);
|
|
205
|
+
const scoreOld = (existing.price !== null ? 1 : 0) + (existing.rating !== null ? 1 : 0);
|
|
206
|
+
if (scoreNew > scoreOld) {
|
|
207
|
+
byName.set(key, hotel);
|
|
208
|
+
}
|
|
209
|
+
else if (scoreNew === scoreOld) {
|
|
210
|
+
// Tiebreak: prefer the one with lower price (or keep existing if equal)
|
|
211
|
+
if (hotel.price !== null && (existing.price === null || hotel.price < existing.price)) {
|
|
212
|
+
byName.set(key, hotel);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return Array.from(byName.values());
|
|
217
|
+
}
|
|
218
|
+
// ── Sorting ───────────────────────────────────────────────────────────────────
|
|
219
|
+
/**
|
|
220
|
+
* Sort hotel results.
|
|
221
|
+
* - price: ascending, nulls last
|
|
222
|
+
* - rating: descending, nulls last
|
|
223
|
+
* - value: rating/price ratio, descending, nulls last
|
|
224
|
+
*/
|
|
225
|
+
export function sortHotels(hotels, sort) {
|
|
226
|
+
const sorted = [...hotels];
|
|
227
|
+
switch (sort) {
|
|
228
|
+
case 'price':
|
|
229
|
+
sorted.sort((a, b) => {
|
|
230
|
+
if (a.price === null && b.price === null)
|
|
231
|
+
return 0;
|
|
232
|
+
if (a.price === null)
|
|
233
|
+
return 1;
|
|
234
|
+
if (b.price === null)
|
|
235
|
+
return -1;
|
|
236
|
+
return a.price - b.price;
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
case 'rating':
|
|
240
|
+
sorted.sort((a, b) => {
|
|
241
|
+
if (a.rating === null && b.rating === null)
|
|
242
|
+
return 0;
|
|
243
|
+
if (a.rating === null)
|
|
244
|
+
return 1;
|
|
245
|
+
if (b.rating === null)
|
|
246
|
+
return -1;
|
|
247
|
+
return b.rating - a.rating;
|
|
248
|
+
});
|
|
249
|
+
break;
|
|
250
|
+
case 'value': {
|
|
251
|
+
const valueOf = (h) => {
|
|
252
|
+
if (h.price === null || h.price === 0 || h.rating === null)
|
|
253
|
+
return null;
|
|
254
|
+
return h.rating / h.price;
|
|
255
|
+
};
|
|
256
|
+
sorted.sort((a, b) => {
|
|
257
|
+
const va = valueOf(a);
|
|
258
|
+
const vb = valueOf(b);
|
|
259
|
+
if (va === null && vb === null)
|
|
260
|
+
return 0;
|
|
261
|
+
if (va === null)
|
|
262
|
+
return 1;
|
|
263
|
+
if (vb === null)
|
|
264
|
+
return -1;
|
|
265
|
+
return vb - va;
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return sorted;
|
|
271
|
+
}
|
|
272
|
+
// ── Main Function ─────────────────────────────────────────────────────────────
|
|
273
|
+
const DEFAULT_SOURCES = ['kayak', 'booking', 'google', 'expedia'];
|
|
274
|
+
const SIMPLE_TIMEOUT = 15_000;
|
|
275
|
+
const BROWSER_TIMEOUT = 30_000;
|
|
276
|
+
const EXPEDIA_TIMEOUT = 60_000;
|
|
277
|
+
/**
|
|
278
|
+
* Search multiple travel sites for hotels and return sorted, deduplicated results.
|
|
279
|
+
*/
|
|
280
|
+
export async function searchHotels(options) {
|
|
281
|
+
const startTime = Date.now();
|
|
282
|
+
// ── Parse dates ────────────────────────────────────────────────────────────
|
|
283
|
+
const checkin = parseDate(options.checkin);
|
|
284
|
+
const rawCheckout = options.checkout;
|
|
285
|
+
const checkout = rawCheckout ? parseDate(rawCheckout) : addDays(checkin, 1);
|
|
286
|
+
const destination = options.destination;
|
|
287
|
+
const sort = options.sort ?? 'price';
|
|
288
|
+
const limit = options.limit ?? 20;
|
|
289
|
+
const allowedSources = new Set((options.sources ?? DEFAULT_SOURCES).map(s => s.toLowerCase()));
|
|
290
|
+
const useGlobalStealth = options.stealth ?? false;
|
|
291
|
+
const proxyUrl = options.proxy;
|
|
292
|
+
// ── Build source URLs ──────────────────────────────────────────────────────
|
|
293
|
+
const allSourceUrls = buildSourceUrls(destination, checkin, checkout).filter(s => allowedSources.has(s.name));
|
|
294
|
+
// ── Fetch all sources in parallel ──────────────────────────────────────────
|
|
295
|
+
const settled = await Promise.allSettled(allSourceUrls.map(async (src) => {
|
|
296
|
+
const isKayak = src.name === 'kayak';
|
|
297
|
+
const isBooking = src.name === 'booking';
|
|
298
|
+
const isExpedia = src.name === 'expedia';
|
|
299
|
+
const useStealth = useGlobalStealth || isKayak || isExpedia;
|
|
300
|
+
const useRender = useStealth || isBooking;
|
|
301
|
+
const timeout = isExpedia ? EXPEDIA_TIMEOUT : (useRender ? BROWSER_TIMEOUT : SIMPLE_TIMEOUT);
|
|
302
|
+
// Expedia is a SPA — wait for property listings to appear before extracting
|
|
303
|
+
const actions = isExpedia
|
|
304
|
+
? [{ type: 'waitForSelector', selector: "[data-stid='property-listing'], li.uitk-spacing" }]
|
|
305
|
+
: undefined;
|
|
306
|
+
const result = await peel(src.url, {
|
|
307
|
+
format: 'html',
|
|
308
|
+
render: useRender,
|
|
309
|
+
stealth: useStealth,
|
|
310
|
+
timeout,
|
|
311
|
+
...(actions ? { actions } : {}),
|
|
312
|
+
...(proxyUrl ? { proxy: proxyUrl } : {}),
|
|
313
|
+
});
|
|
314
|
+
// Prefer CSS schema extraction when a schema is available for this source
|
|
315
|
+
const schema = findSchemaForUrl(src.url);
|
|
316
|
+
const hotels = [];
|
|
317
|
+
if (schema) {
|
|
318
|
+
const schemaItems = extractWithSchema(result.content, schema, src.url);
|
|
319
|
+
for (const item of schemaItems) {
|
|
320
|
+
const mapped = {
|
|
321
|
+
title: typeof item.title === 'string' ? item.title : undefined,
|
|
322
|
+
price: typeof item.price === 'string' ? item.price : undefined,
|
|
323
|
+
rating: typeof item.rating === 'string' ? item.rating : undefined,
|
|
324
|
+
link: typeof item.link === 'string' ? item.link : undefined,
|
|
325
|
+
image: typeof item.image === 'string' ? item.image : undefined,
|
|
326
|
+
description: typeof item.location === 'string' ? item.location : undefined,
|
|
327
|
+
};
|
|
328
|
+
const hotel = normaliseToHotelResult(mapped, src.name);
|
|
329
|
+
if (hotel)
|
|
330
|
+
hotels.push(hotel);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Fall back to generic extraction if schema yielded nothing
|
|
334
|
+
if (hotels.length === 0) {
|
|
335
|
+
const listings = extractListings(result.content, src.url);
|
|
336
|
+
for (const item of listings) {
|
|
337
|
+
const hotel = normaliseToHotelResult(item, src.name);
|
|
338
|
+
if (hotel)
|
|
339
|
+
hotels.push(hotel);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return { name: src.name, hotels };
|
|
343
|
+
}));
|
|
344
|
+
// ── Collect per-source status and results ──────────────────────────────────
|
|
345
|
+
const sourceStats = [];
|
|
346
|
+
const allHotels = [];
|
|
347
|
+
for (let i = 0; i < allSourceUrls.length; i++) {
|
|
348
|
+
const src = allSourceUrls[i];
|
|
349
|
+
const outcome = settled[i];
|
|
350
|
+
if (outcome.status === 'fulfilled') {
|
|
351
|
+
const { hotels } = outcome.value;
|
|
352
|
+
sourceStats.push({ name: src.name, count: hotels.length, status: 'ok' });
|
|
353
|
+
allHotels.push(...hotels);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
const errMsg = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);
|
|
357
|
+
const isBlocked = errMsg.toLowerCase().includes('blocked') ||
|
|
358
|
+
errMsg.toLowerCase().includes('403') ||
|
|
359
|
+
errMsg.toLowerCase().includes('cloudflare');
|
|
360
|
+
sourceStats.push({
|
|
361
|
+
name: src.name,
|
|
362
|
+
count: 0,
|
|
363
|
+
status: isBlocked ? 'blocked' : 'error',
|
|
364
|
+
error: errMsg,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// ── Deduplicate, sort, limit ───────────────────────────────────────────────
|
|
369
|
+
const unique = deduplicateHotels(allHotels);
|
|
370
|
+
const sorted = sortHotels(unique, sort);
|
|
371
|
+
const results = sorted.slice(0, limit);
|
|
372
|
+
const elapsed = Date.now() - startTime;
|
|
373
|
+
return {
|
|
374
|
+
destination,
|
|
375
|
+
checkin,
|
|
376
|
+
checkout,
|
|
377
|
+
totalResults: results.length,
|
|
378
|
+
results,
|
|
379
|
+
sources: sourceStats,
|
|
380
|
+
elapsed,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure HTTP fetching — no browser dependencies.
|
|
3
|
+
* Handles connection pooling, conditional caching, SSRF validation, and simpleFetch.
|
|
4
|
+
*/
|
|
5
|
+
export declare function closePool(): Promise<void>;
|
|
6
|
+
export declare function createAbortError(): Error;
|
|
7
|
+
/**
|
|
8
|
+
* Domains known to aggressively block datacenter IPs.
|
|
9
|
+
* Requests to these domains automatically route through the Webshare residential
|
|
10
|
+
* proxy when proxy credentials are configured (WEBSHARE_PROXY_* env vars).
|
|
11
|
+
*/
|
|
12
|
+
export declare const PROXY_PREFERRED_DOMAINS: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Returns true if the URL's domain is on the proxy-preferred blocklist.
|
|
15
|
+
* Matches exact hostname (sans www.) and all subdomains.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* shouldUseProxy('https://www.reddit.com/r/news') // true
|
|
19
|
+
* shouldUseProxy('https://example.com') // false
|
|
20
|
+
*/
|
|
21
|
+
export declare function shouldUseProxy(url: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Generate browser-like request headers tailored to the User-Agent type.
|
|
24
|
+
*
|
|
25
|
+
* - Chrome/Edge: full Sec-CH-UA + Sec-Fetch-* header set
|
|
26
|
+
* - Firefox: adjusted Accept, TE header, partial Sec-Fetch-* (no Sec-CH-UA)
|
|
27
|
+
* - Safari: minimal headers, no Sec-Fetch-* or Sec-CH-UA
|
|
28
|
+
* - Other: basic headers only
|
|
29
|
+
*
|
|
30
|
+
* Automatically adds a Google referer for domains where it helps bypass blocks.
|
|
31
|
+
*
|
|
32
|
+
* @param url - Target URL (used for domain-specific header additions)
|
|
33
|
+
* @param userAgent - User-Agent string (determines which header set is applied)
|
|
34
|
+
*/
|
|
35
|
+
export declare function getStealthHeaders(url: string, userAgent: string): Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* SECURITY: Validate URL to prevent SSRF attacks
|
|
38
|
+
* Blocks localhost, private IPs, link-local, and various bypass techniques
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateUrl(urlString: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Validate and sanitize user agent string
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateUserAgent(userAgent: string): string;
|
|
45
|
+
export interface FetchResult {
|
|
46
|
+
/** Text content (HTML/JSON/XML/plain text). For binary documents, this may be an empty string. */
|
|
47
|
+
html: string;
|
|
48
|
+
/** Raw response body (used for binary documents like PDFs/DOCX). */
|
|
49
|
+
buffer?: Buffer;
|
|
50
|
+
url: string;
|
|
51
|
+
statusCode?: number;
|
|
52
|
+
screenshot?: Buffer;
|
|
53
|
+
/** Raw Content-Type header from the response (may include charset). */
|
|
54
|
+
contentType?: string;
|
|
55
|
+
/** Selected response headers for freshness metadata (last-modified, etag, cache-control). */
|
|
56
|
+
responseHeaders?: Record<string, string>;
|
|
57
|
+
/** Playwright page object (only available in browser/stealth mode, must be closed by caller) */
|
|
58
|
+
page?: import('playwright').Page;
|
|
59
|
+
/** Playwright browser object (only available in browser/stealth mode, must be closed by caller) */
|
|
60
|
+
browser?: import('playwright').Browser;
|
|
61
|
+
/** Auto-interact result: what cookie banners / overlays were dismissed before extraction */
|
|
62
|
+
autoInteract?: import('./auto-interact.js').AutoInteractResult;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Simple HTTP fetch using native fetch + Cheerio
|
|
66
|
+
* Fast and lightweight, but can be blocked by Cloudflare/bot detection
|
|
67
|
+
* SECURITY: Manual redirect handling with SSRF re-validation
|
|
68
|
+
*/
|
|
69
|
+
export declare function simpleFetch(url: string, userAgent?: string, timeoutMs?: number, customHeaders?: Record<string, string>, abortSignal?: AbortSignal, proxy?: string, proxyContext?: {
|
|
70
|
+
userId?: string;
|
|
71
|
+
tier?: string;
|
|
72
|
+
}): Promise<FetchResult>;
|