@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,81 @@
|
|
|
1
|
+
export interface SearchIntent {
|
|
2
|
+
type: 'cars' | 'flights' | 'hotels' | 'rental' | 'restaurants' | 'products' | 'general';
|
|
3
|
+
query: string;
|
|
4
|
+
params: Record<string, string>;
|
|
5
|
+
/** Suggested domain sources for this intent — hints for result boosting, not filtering */
|
|
6
|
+
suggestedDomains?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface VerdictOption {
|
|
9
|
+
provider: string;
|
|
10
|
+
price: number;
|
|
11
|
+
currency: string;
|
|
12
|
+
route?: string;
|
|
13
|
+
url: string;
|
|
14
|
+
notes?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TransactionalVerdict {
|
|
17
|
+
/** Vertical / category — e.g. 'transit', 'gas', 'travel', 'equipment_rental' */
|
|
18
|
+
vertical: string;
|
|
19
|
+
/** Human-readable headline — e.g. "Cheapest I found is $19.00 on FlixBus for New York → Boston" */
|
|
20
|
+
headline: string;
|
|
21
|
+
/** How confident we are in the data (mirrors existing confidence field) */
|
|
22
|
+
confidence: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
23
|
+
/** Best option found across all sources */
|
|
24
|
+
bestOption: VerdictOption;
|
|
25
|
+
/** Other options found, sorted cheapest-first */
|
|
26
|
+
alternatives: VerdictOption[];
|
|
27
|
+
/** Aggregate totals when applicable (round-trip, etc.) */
|
|
28
|
+
totals?: {
|
|
29
|
+
oneWayLowest?: number;
|
|
30
|
+
returnLowest?: number;
|
|
31
|
+
roundTripLowest?: number;
|
|
32
|
+
currency: string;
|
|
33
|
+
};
|
|
34
|
+
/** Caveats/disclaimers about the data */
|
|
35
|
+
caveats: string[];
|
|
36
|
+
/** Parsed query parameters (origin, destination, dates, etc.) */
|
|
37
|
+
query?: {
|
|
38
|
+
origin?: string;
|
|
39
|
+
destination?: string;
|
|
40
|
+
departDate?: string;
|
|
41
|
+
returnDate?: string;
|
|
42
|
+
isRoundTrip?: boolean;
|
|
43
|
+
mode?: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface SmartSearchResult {
|
|
47
|
+
type: 'cars' | 'flights' | 'hotels' | 'rental' | 'restaurants' | 'products' | 'general';
|
|
48
|
+
source: string;
|
|
49
|
+
sourceUrl: string;
|
|
50
|
+
content: string;
|
|
51
|
+
title?: string;
|
|
52
|
+
domainData?: any;
|
|
53
|
+
structured?: any;
|
|
54
|
+
results?: any[];
|
|
55
|
+
tokens: number;
|
|
56
|
+
fetchTimeMs: number;
|
|
57
|
+
loadingMessage?: string;
|
|
58
|
+
answer?: string;
|
|
59
|
+
confidence?: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
60
|
+
sources?: Array<{
|
|
61
|
+
title: string;
|
|
62
|
+
url: string;
|
|
63
|
+
domain: string;
|
|
64
|
+
}>;
|
|
65
|
+
timing?: {
|
|
66
|
+
searchMs: number;
|
|
67
|
+
peelMs: number;
|
|
68
|
+
llmMs: number;
|
|
69
|
+
};
|
|
70
|
+
mapUrl?: string;
|
|
71
|
+
safety?: {
|
|
72
|
+
verified: boolean;
|
|
73
|
+
promptInjectionsBlocked: number;
|
|
74
|
+
maliciousPatternsStripped: number;
|
|
75
|
+
sourcesChecked: number;
|
|
76
|
+
};
|
|
77
|
+
/** Suggested authoritative domains for this query (financial → reuters, etc.) */
|
|
78
|
+
suggestedDomains?: string[];
|
|
79
|
+
/** Structured verdict for transactional queries (transit, gas, travel, etc.) */
|
|
80
|
+
verdict?: TransactionalVerdict;
|
|
81
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type RequestedStore = {
|
|
2
|
+
id: 'amazon' | 'walmart' | 'bestbuy' | 'target' | 'ebay' | 'etsy' | 'traderjoes' | 'costco' | 'bjs' | 'dyson';
|
|
3
|
+
store: string;
|
|
4
|
+
domain: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function detectRequestedStore(query: string): RequestedStore | null;
|
|
7
|
+
export declare function stripRequestedStoreFromQuery(query: string, requestedStore?: RequestedStore | null): string;
|
|
8
|
+
export declare function isRequestedStoreUrl(url: string, requestedStore?: RequestedStore | null): boolean;
|
|
9
|
+
export declare function addAffiliateTag(url: string): string;
|
|
10
|
+
export declare const SHOPPING_DOMAINS: Array<{
|
|
11
|
+
pattern: string;
|
|
12
|
+
name: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function getStoreInfo(url: string): {
|
|
15
|
+
store: string;
|
|
16
|
+
domain: string;
|
|
17
|
+
} | null;
|
|
18
|
+
export declare function parsePrice(text: string): string | undefined;
|
|
19
|
+
export declare function extractPriceValue(priceStr: string | undefined): number | undefined;
|
|
20
|
+
export declare function cleanProductTitle(title: string): string;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const AFFILIATE_TAGS = {
|
|
2
|
+
'amazon.com': { param: 'tag', value: process.env.AMAZON_AFFILIATE_TAG || '' },
|
|
3
|
+
'walmart.com': { param: 'wmlspartner', value: process.env.WALMART_AFFILIATE_ID || '' },
|
|
4
|
+
'bestbuy.com': { param: 'ref', value: process.env.BESTBUY_AFFILIATE_ID || '' },
|
|
5
|
+
'target.com': { param: 'afid', value: process.env.TARGET_AFFILIATE_ID || '' },
|
|
6
|
+
'ebay.com': { param: 'campid', value: process.env.EBAY_AFFILIATE_ID || '' },
|
|
7
|
+
'etsy.com': { param: 'ref', value: process.env.ETSY_AFFILIATE_ID || '' },
|
|
8
|
+
'booking.com': { param: 'aid', value: process.env.BOOKING_AFFILIATE_ID || '' },
|
|
9
|
+
'kayak.com': { param: 'affid', value: process.env.KAYAK_AFFILIATE_ID || '' },
|
|
10
|
+
'expedia.com': { param: 'affcid', value: process.env.EXPEDIA_AFFILIATE_ID || '' },
|
|
11
|
+
};
|
|
12
|
+
const DOMAIN_TO_STORE = {
|
|
13
|
+
'amazon.com': 'amazon', 'walmart.com': 'walmart', 'bestbuy.com': 'bestbuy',
|
|
14
|
+
'target.com': 'target', 'ebay.com': 'ebay', 'etsy.com': 'etsy',
|
|
15
|
+
'booking.com': 'booking', 'kayak.com': 'kayak', 'expedia.com': 'expedia',
|
|
16
|
+
};
|
|
17
|
+
const REQUESTED_STORES = [
|
|
18
|
+
{ id: 'bestbuy', store: 'Best Buy', domain: 'bestbuy.com', patterns: [/\bbest[\s-]*buy(?:\.com)?\b/i] },
|
|
19
|
+
{ id: 'walmart', store: 'Walmart', domain: 'walmart.com', patterns: [/\bwal(?:\s|-)?mart(?:\.com)?\b/i] },
|
|
20
|
+
{ id: 'target', store: 'Target', domain: 'target.com', patterns: [/\btarget(?:\.com)?\b/i] },
|
|
21
|
+
{ id: 'amazon', store: 'Amazon', domain: 'amazon.com', patterns: [/\bamazon(?:\.com)?\b/i] },
|
|
22
|
+
{ id: 'costco', store: 'Costco', domain: 'costco.com', patterns: [/\bcostco(?:\.com)?\b/i] },
|
|
23
|
+
{ id: 'bjs', store: "BJ's Wholesale Club", domain: 'bjs.com', patterns: [/\bbj'?s(?:\s+wholesale(?:\s+club)?)?(?:\.com)?\b/i] },
|
|
24
|
+
{ id: 'traderjoes', store: "Trader Joe's", domain: 'traderjoes.com', patterns: [/\btrader\s+joe'?s(?:\.com)?\b/i] },
|
|
25
|
+
{ id: 'dyson', store: 'Dyson', domain: 'dyson.com', patterns: [/\bdyson(?:\s+store|\.com)\b/i] },
|
|
26
|
+
{ id: 'ebay', store: 'eBay', domain: 'ebay.com', patterns: [/\be(?:\s|-)?bay(?:\.com)?\b/i] },
|
|
27
|
+
{ id: 'etsy', store: 'Etsy', domain: 'etsy.com', patterns: [/\betsy(?:\.com)?\b/i] },
|
|
28
|
+
];
|
|
29
|
+
export function detectRequestedStore(query) {
|
|
30
|
+
const normalized = query.toLowerCase();
|
|
31
|
+
for (const store of REQUESTED_STORES) {
|
|
32
|
+
if (store.patterns.some(pattern => pattern.test(normalized))) {
|
|
33
|
+
return { id: store.id, store: store.store, domain: store.domain };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
export function stripRequestedStoreFromQuery(query, requestedStore) {
|
|
39
|
+
if (!query)
|
|
40
|
+
return query;
|
|
41
|
+
const stores = requestedStore
|
|
42
|
+
? REQUESTED_STORES.filter(store => store.id === requestedStore.id)
|
|
43
|
+
: REQUESTED_STORES;
|
|
44
|
+
let stripped = query;
|
|
45
|
+
for (const store of stores) {
|
|
46
|
+
for (const pattern of store.patterns) {
|
|
47
|
+
const globalPattern = new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`);
|
|
48
|
+
stripped = stripped.replace(globalPattern, ' ');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return stripped.replace(/\s+/g, ' ').trim();
|
|
52
|
+
}
|
|
53
|
+
export function isRequestedStoreUrl(url, requestedStore) {
|
|
54
|
+
if (!requestedStore)
|
|
55
|
+
return true;
|
|
56
|
+
try {
|
|
57
|
+
const hostname = new URL(url).hostname.replace(/^www\./, '');
|
|
58
|
+
return hostname === requestedStore.domain || hostname.endsWith(`.${requestedStore.domain}`);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function addAffiliateTag(url) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = new URL(url);
|
|
67
|
+
const hostname = parsed.hostname.replace('www.', '');
|
|
68
|
+
for (const [domain] of Object.entries(DOMAIN_TO_STORE)) {
|
|
69
|
+
if ((hostname === domain || hostname.endsWith('.' + domain)) && AFFILIATE_TAGS[domain]?.value) {
|
|
70
|
+
const apiUrl = process.env.API_URL || 'https://api.webpeel.dev';
|
|
71
|
+
return `${apiUrl}/go?url=${encodeURIComponent(url)}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch { /* invalid URL — return as-is */ }
|
|
76
|
+
return url;
|
|
77
|
+
}
|
|
78
|
+
export const SHOPPING_DOMAINS = [
|
|
79
|
+
{ pattern: 'amazon.com', name: 'Amazon' }, { pattern: 'bestbuy.com', name: 'Best Buy' },
|
|
80
|
+
{ pattern: 'walmart.com', name: 'Walmart' }, { pattern: 'target.com', name: 'Target' },
|
|
81
|
+
{ pattern: 'costco.com', name: 'Costco' }, { pattern: 'bjs.com', name: "BJ's Wholesale Club" },
|
|
82
|
+
{ pattern: 'traderjoes.com', name: "Trader Joe's" }, { pattern: 'dyson.com', name: 'Dyson' },
|
|
83
|
+
{ pattern: 'zappos.com', name: 'Zappos' }, { pattern: 'rei.com', name: 'REI' },
|
|
84
|
+
{ pattern: 'nordstrom.com', name: 'Nordstrom' }, { pattern: 'macys.com', name: "Macy's" },
|
|
85
|
+
{ pattern: 'sephora.com', name: 'Sephora' }, { pattern: 'ulta.com', name: 'Ulta' },
|
|
86
|
+
{ pattern: 'homedepot.com', name: 'Home Depot' }, { pattern: 'lowes.com', name: "Lowe's" },
|
|
87
|
+
{ pattern: 'ebay.com', name: 'eBay' }, { pattern: 'etsy.com', name: 'Etsy' },
|
|
88
|
+
{ pattern: 'tcgplayer.com', name: 'TCGPlayer' }, { pattern: 'cardmarket.com', name: 'Cardmarket' },
|
|
89
|
+
{ pattern: 'mercari.com', name: 'Mercari' }, { pattern: 'facebook.com', name: 'Facebook Marketplace' },
|
|
90
|
+
{ pattern: 'uline.com', name: 'Uline' }, { pattern: 'alibaba.com', name: 'Alibaba' },
|
|
91
|
+
{ pattern: 'webstaurantstore.com', name: 'WebstaurantStore' }, { pattern: 'globalindustrial.com', name: 'Global Industrial' },
|
|
92
|
+
{ pattern: 'staples.com', name: 'Staples' }, { pattern: 'instacart.com', name: 'Instacart' },
|
|
93
|
+
{ pattern: 'freshdirect.com', name: 'FreshDirect' }, { pattern: 'wholefoodsmarket.com', name: 'Whole Foods' },
|
|
94
|
+
];
|
|
95
|
+
export function getStoreInfo(url) {
|
|
96
|
+
try {
|
|
97
|
+
const hostname = new URL(url).hostname.replace('www.', '');
|
|
98
|
+
for (const s of SHOPPING_DOMAINS) {
|
|
99
|
+
if (hostname === s.pattern || hostname.endsWith('.' + s.pattern))
|
|
100
|
+
return { store: s.name, domain: s.pattern };
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function parsePrice(text) {
|
|
109
|
+
if (!text)
|
|
110
|
+
return undefined;
|
|
111
|
+
const rangeMatch = text.match(/\$\s*([\d,]+(?:\.\d{2})?)\s*[-–—to]+\s*\$\s*([\d,]+(?:\.\d{2})?)/i);
|
|
112
|
+
if (rangeMatch) {
|
|
113
|
+
const lo = rangeMatch[1].replace(/,/g, '');
|
|
114
|
+
return `from $${parseFloat(lo).toLocaleString('en-US', { minimumFractionDigits: 0 })}`;
|
|
115
|
+
}
|
|
116
|
+
const fromMatch = text.match(/from\s+\$\s*([\d,]+(?:\.\d{2})?)/i);
|
|
117
|
+
if (fromMatch) {
|
|
118
|
+
const val = parseFloat(fromMatch[1].replace(/,/g, ''));
|
|
119
|
+
return `from $${val.toLocaleString('en-US', { minimumFractionDigits: 0 })}`;
|
|
120
|
+
}
|
|
121
|
+
const plainMatch = text.match(/\$\s*([\d,]+(?:\.\d{2})?)/);
|
|
122
|
+
if (plainMatch) {
|
|
123
|
+
const val = parseFloat(plainMatch[1].replace(/,/g, ''));
|
|
124
|
+
if (isNaN(val))
|
|
125
|
+
return undefined;
|
|
126
|
+
if (val > 50000)
|
|
127
|
+
return undefined;
|
|
128
|
+
return `$${val.toLocaleString('en-US', { minimumFractionDigits: val % 1 !== 0 ? 2 : 0 })}`;
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
export function extractPriceValue(priceStr) {
|
|
133
|
+
if (!priceStr)
|
|
134
|
+
return undefined;
|
|
135
|
+
const match = priceStr.match(/\$\s*([\d,]+(?:\.\d+)?)/);
|
|
136
|
+
return match ? parseFloat(match[1].replace(/,/g, '')) : undefined;
|
|
137
|
+
}
|
|
138
|
+
export function cleanProductTitle(title) {
|
|
139
|
+
return title
|
|
140
|
+
.replace(/^amazon\.com\s*[:\-–—]\s*/i, '')
|
|
141
|
+
.replace(/^walmart\s*[:\-–—]\s*/i, '')
|
|
142
|
+
.replace(/^target\s*[:\-–—]\s*/i, '')
|
|
143
|
+
.replace(/^best\s*buy\s*[:\-–—]\s*/i, '')
|
|
144
|
+
.replace(/^ebay\s*[:\-–—]\s*/i, '')
|
|
145
|
+
.trim();
|
|
146
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stats endpoint - provides dashboard statistics
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import { PostgresAuthStore } from '../pg-auth-store.js';
|
|
6
|
+
export function createStatsRouter(authStore) {
|
|
7
|
+
const router = Router();
|
|
8
|
+
router.get('/v1/stats', async (req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
// Require authentication (API key or JWT session token)
|
|
11
|
+
const userId = req.auth?.keyInfo?.accountId || req.user?.userId;
|
|
12
|
+
if (!userId) {
|
|
13
|
+
res.status(401).json({ success: false, error: { type: 'unauthorized', message: 'Authentication required.', docs: 'https://webpeel.dev/docs/authentication' }, requestId: req.requestId });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Only works with PostgreSQL backend
|
|
17
|
+
if (!(authStore instanceof PostgresAuthStore)) {
|
|
18
|
+
res.status(501).json({
|
|
19
|
+
success: false,
|
|
20
|
+
error: {
|
|
21
|
+
type: 'not_implemented',
|
|
22
|
+
message: 'Stats endpoint requires PostgreSQL backend',
|
|
23
|
+
docs: 'https://webpeel.dev/docs/errors#not_implemented',
|
|
24
|
+
},
|
|
25
|
+
requestId: req.requestId,
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Access pool via any cast (pool is private but we need direct DB access)
|
|
30
|
+
const pgStore = authStore;
|
|
31
|
+
// Get stats from usage_logs table
|
|
32
|
+
const statsQuery = `
|
|
33
|
+
SELECT
|
|
34
|
+
COUNT(*) as total_requests,
|
|
35
|
+
AVG(CASE WHEN status_code >= 200 AND status_code < 300 THEN 1.0 ELSE 0.0 END) * 100 as success_rate,
|
|
36
|
+
AVG(processing_time_ms) as avg_response_time
|
|
37
|
+
FROM usage_logs
|
|
38
|
+
WHERE user_id = $1
|
|
39
|
+
`;
|
|
40
|
+
const result = await pgStore.pool.query(statsQuery, [userId]);
|
|
41
|
+
if (result.rows.length === 0) {
|
|
42
|
+
// No data yet - return defaults
|
|
43
|
+
res.json({
|
|
44
|
+
totalRequests: 0,
|
|
45
|
+
successRate: 100,
|
|
46
|
+
avgResponseTime: 0,
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const row = result.rows[0];
|
|
51
|
+
res.json({
|
|
52
|
+
totalRequests: parseInt(row.total_requests) || 0,
|
|
53
|
+
successRate: parseFloat(row.success_rate) || 100,
|
|
54
|
+
avgResponseTime: Math.round(parseFloat(row.avg_response_time)) || 0,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('Stats error:', error);
|
|
59
|
+
res.status(500).json({
|
|
60
|
+
success: false,
|
|
61
|
+
error: {
|
|
62
|
+
type: 'internal_error',
|
|
63
|
+
message: 'Failed to retrieve stats',
|
|
64
|
+
docs: 'https://webpeel.dev/docs/errors#internal_error',
|
|
65
|
+
},
|
|
66
|
+
requestId: req.requestId,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return router;
|
|
71
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe webhook handler for subscription management
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import pg from 'pg';
|
|
6
|
+
/**
|
|
7
|
+
* Create Stripe Billing Portal router
|
|
8
|
+
* POST /v1/billing/portal — create a Stripe Customer Portal session
|
|
9
|
+
* Requires global auth middleware to already have run (req.user or req.auth set).
|
|
10
|
+
*/
|
|
11
|
+
export declare function createBillingPortalRouter(pool: pg.Pool | null): Router;
|
|
12
|
+
/**
|
|
13
|
+
* Create Stripe webhook router
|
|
14
|
+
*/
|
|
15
|
+
export declare function createStripeRouter(): Router;
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe webhook handler for subscription management
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from 'express';
|
|
5
|
+
import Stripe from 'stripe';
|
|
6
|
+
import pg from 'pg';
|
|
7
|
+
import { createLogger } from '../logger.js';
|
|
8
|
+
const log = createLogger('stripe');
|
|
9
|
+
const { Pool } = pg;
|
|
10
|
+
/**
|
|
11
|
+
* Tier configuration (weekly usage model)
|
|
12
|
+
*/
|
|
13
|
+
const TIER_LIMITS = {
|
|
14
|
+
free: { weekly_limit: 500, burst_limit: 50, rate_limit: 10 },
|
|
15
|
+
pro: { weekly_limit: 1250, burst_limit: 100, rate_limit: 60 },
|
|
16
|
+
max: { weekly_limit: 6250, burst_limit: 500, rate_limit: 200 },
|
|
17
|
+
admin: { weekly_limit: 100000, burst_limit: 10000, rate_limit: 1000 },
|
|
18
|
+
enterprise: { weekly_limit: 50000, burst_limit: 2000, rate_limit: 500 },
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Create Stripe Billing Portal router
|
|
22
|
+
* POST /v1/billing/portal — create a Stripe Customer Portal session
|
|
23
|
+
* Requires global auth middleware to already have run (req.user or req.auth set).
|
|
24
|
+
*/
|
|
25
|
+
export function createBillingPortalRouter(pool) {
|
|
26
|
+
const router = Router();
|
|
27
|
+
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
|
|
28
|
+
if (!stripeSecretKey) {
|
|
29
|
+
log.warn('STRIPE_SECRET_KEY not configured - billing portal disabled');
|
|
30
|
+
return router;
|
|
31
|
+
}
|
|
32
|
+
const stripe = new Stripe(stripeSecretKey);
|
|
33
|
+
router.post('/v1/billing/portal', async (req, res) => {
|
|
34
|
+
try {
|
|
35
|
+
const userId = req.user?.userId || req.auth?.keyInfo?.accountId;
|
|
36
|
+
if (!userId) {
|
|
37
|
+
res.status(401).json({ success: false, error: { type: 'unauthorized', message: 'Authentication required.', docs: 'https://webpeel.dev/docs/authentication' }, requestId: req.requestId });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!pool) {
|
|
41
|
+
res.status(503).json({
|
|
42
|
+
success: false,
|
|
43
|
+
error: {
|
|
44
|
+
type: 'db_unavailable',
|
|
45
|
+
message: 'Database not configured',
|
|
46
|
+
docs: 'https://webpeel.dev/docs/errors#db_unavailable',
|
|
47
|
+
},
|
|
48
|
+
requestId: req.requestId,
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Get user's stripe_customer_id from DB
|
|
53
|
+
const result = await pool.query('SELECT stripe_customer_id FROM users WHERE id = $1', [userId]);
|
|
54
|
+
const stripeCustomerId = result.rows[0]?.stripe_customer_id;
|
|
55
|
+
if (!stripeCustomerId) {
|
|
56
|
+
res.status(400).json({ success: false, error: { type: 'no_subscription', message: 'No active subscription found. Upgrade to Pro or Max to manage billing.', hint: 'Upgrade at https://webpeel.dev/pricing', docs: 'https://webpeel.dev/docs/errors#no_subscription' }, requestId: req.requestId });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Create portal session
|
|
60
|
+
const session = await stripe.billingPortal.sessions.create({
|
|
61
|
+
customer: stripeCustomerId,
|
|
62
|
+
return_url: 'https://app.webpeel.dev/billing',
|
|
63
|
+
});
|
|
64
|
+
res.json({ url: session.url });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
log.error('Failed to create portal session:', err);
|
|
68
|
+
res.status(500).json({
|
|
69
|
+
success: false,
|
|
70
|
+
error: {
|
|
71
|
+
type: 'portal_failed',
|
|
72
|
+
message: 'Failed to create billing portal session',
|
|
73
|
+
docs: 'https://webpeel.dev/docs/errors#portal_failed',
|
|
74
|
+
},
|
|
75
|
+
requestId: req.requestId,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return router;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create Stripe webhook router
|
|
83
|
+
*/
|
|
84
|
+
export function createStripeRouter() {
|
|
85
|
+
const router = Router();
|
|
86
|
+
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
|
|
87
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
|
88
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
89
|
+
if (!stripeSecretKey) {
|
|
90
|
+
log.warn('STRIPE_SECRET_KEY not configured - Stripe webhooks disabled');
|
|
91
|
+
return router;
|
|
92
|
+
}
|
|
93
|
+
if (!webhookSecret) {
|
|
94
|
+
log.warn('STRIPE_WEBHOOK_SECRET not configured - Stripe webhooks disabled');
|
|
95
|
+
return router;
|
|
96
|
+
}
|
|
97
|
+
if (!dbUrl) {
|
|
98
|
+
throw new Error('DATABASE_URL environment variable is required');
|
|
99
|
+
}
|
|
100
|
+
const stripe = new Stripe(stripeSecretKey);
|
|
101
|
+
const pool = new Pool({
|
|
102
|
+
connectionString: dbUrl,
|
|
103
|
+
// TLS: enabled when DATABASE_URL contains sslmode=require.
|
|
104
|
+
// Secure by default (rejectUnauthorized: true); set PG_REJECT_UNAUTHORIZED=false
|
|
105
|
+
// only for managed DBs (Render/Neon/Supabase) that use self-signed certs.
|
|
106
|
+
ssl: process.env.DATABASE_URL?.includes('sslmode=require')
|
|
107
|
+
? { rejectUnauthorized: process.env.PG_REJECT_UNAUTHORIZED !== 'false' }
|
|
108
|
+
: undefined,
|
|
109
|
+
});
|
|
110
|
+
/**
|
|
111
|
+
* POST /v1/webhooks/stripe
|
|
112
|
+
* Handle Stripe webhook events
|
|
113
|
+
* SECURITY: Verifies webhook signature
|
|
114
|
+
*/
|
|
115
|
+
router.post('/', async (req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
const sig = req.headers['stripe-signature'];
|
|
118
|
+
if (!sig || typeof sig !== 'string') {
|
|
119
|
+
res.status(400).json({ success: false, error: { type: 'missing_signature', message: 'Stripe signature header missing', hint: 'Ensure the request includes the stripe-signature header', docs: 'https://webpeel.dev/docs/errors#missing_signature' }, requestId: req.requestId });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// SECURITY: Verify webhook signature
|
|
123
|
+
let event;
|
|
124
|
+
try {
|
|
125
|
+
event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
log.error('Webhook signature verification failed', { message: err.message });
|
|
129
|
+
res.status(400).json({ success: false, error: { type: 'invalid_signature', message: 'Webhook signature verification failed', hint: 'Verify your STRIPE_WEBHOOK_SECRET matches the Stripe dashboard', docs: 'https://webpeel.dev/docs/errors#invalid_signature' }, requestId: req.requestId });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Handle different event types
|
|
133
|
+
switch (event.type) {
|
|
134
|
+
case 'checkout.session.completed': {
|
|
135
|
+
const session = event.data.object;
|
|
136
|
+
await handleCheckoutCompleted(pool, session);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case 'customer.subscription.updated': {
|
|
140
|
+
const subscription = event.data.object;
|
|
141
|
+
await handleSubscriptionUpdated(pool, subscription);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case 'customer.subscription.deleted': {
|
|
145
|
+
const subscription = event.data.object;
|
|
146
|
+
await handleSubscriptionDeleted(pool, subscription);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case 'invoice.payment_failed': {
|
|
150
|
+
const invoice = event.data.object;
|
|
151
|
+
await handlePaymentFailed(pool, invoice);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
default:
|
|
155
|
+
log.warn(`Unhandled Stripe event type: ${event.type}`);
|
|
156
|
+
}
|
|
157
|
+
res.json({ received: true });
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
log.error('Webhook error', { error: error instanceof Error ? error.message : String(error) });
|
|
161
|
+
res.status(500).json({
|
|
162
|
+
success: false,
|
|
163
|
+
error: {
|
|
164
|
+
type: 'webhook_failed',
|
|
165
|
+
message: 'Failed to process webhook',
|
|
166
|
+
docs: 'https://webpeel.dev/docs/errors#webhook_failed',
|
|
167
|
+
},
|
|
168
|
+
requestId: req.requestId,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return router;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Handle checkout.session.completed
|
|
176
|
+
* Upgrade user tier and set limits
|
|
177
|
+
*/
|
|
178
|
+
async function handleCheckoutCompleted(pool, session) {
|
|
179
|
+
try {
|
|
180
|
+
const customerId = session.customer;
|
|
181
|
+
const subscriptionId = session.subscription;
|
|
182
|
+
// Get subscription to determine tier
|
|
183
|
+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
|
|
184
|
+
const subscription = await stripe.subscriptions.retrieve(subscriptionId);
|
|
185
|
+
// Determine tier from price ID (you'll need to configure these)
|
|
186
|
+
const priceId = subscription.items.data[0]?.price.id;
|
|
187
|
+
const tier = getTierFromPriceId(priceId);
|
|
188
|
+
const limits = TIER_LIMITS[tier];
|
|
189
|
+
// Update user
|
|
190
|
+
await pool.query(`UPDATE users
|
|
191
|
+
SET
|
|
192
|
+
stripe_customer_id = $1,
|
|
193
|
+
stripe_subscription_id = $2,
|
|
194
|
+
tier = $3,
|
|
195
|
+
weekly_limit = $4,
|
|
196
|
+
burst_limit = $5,
|
|
197
|
+
rate_limit = $6,
|
|
198
|
+
updated_at = now()
|
|
199
|
+
WHERE stripe_customer_id = $1 OR email = $7`, [
|
|
200
|
+
customerId,
|
|
201
|
+
subscriptionId,
|
|
202
|
+
tier,
|
|
203
|
+
limits.weekly_limit,
|
|
204
|
+
limits.burst_limit,
|
|
205
|
+
limits.rate_limit,
|
|
206
|
+
session.customer_email,
|
|
207
|
+
]);
|
|
208
|
+
log.info(`Checkout completed for customer ${customerId}: upgraded to ${tier}`);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
log.error('Failed to handle checkout completion', { error: error instanceof Error ? error.message : String(error) });
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Handle customer.subscription.updated
|
|
217
|
+
* Update user tier based on subscription changes
|
|
218
|
+
*/
|
|
219
|
+
async function handleSubscriptionUpdated(pool, subscription) {
|
|
220
|
+
try {
|
|
221
|
+
const customerId = subscription.customer;
|
|
222
|
+
const priceId = subscription.items.data[0]?.price.id;
|
|
223
|
+
const tier = getTierFromPriceId(priceId);
|
|
224
|
+
const limits = TIER_LIMITS[tier];
|
|
225
|
+
await pool.query(`UPDATE users
|
|
226
|
+
SET
|
|
227
|
+
tier = $1,
|
|
228
|
+
weekly_limit = $2,
|
|
229
|
+
burst_limit = $3,
|
|
230
|
+
rate_limit = $4,
|
|
231
|
+
stripe_subscription_id = $5,
|
|
232
|
+
updated_at = now()
|
|
233
|
+
WHERE stripe_customer_id = $6`, [tier, limits.weekly_limit, limits.burst_limit, limits.rate_limit, subscription.id, customerId]);
|
|
234
|
+
log.info(`Subscription updated for customer ${customerId}: tier=${tier}`);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
log.error('Failed to handle subscription update', { error: error instanceof Error ? error.message : String(error) });
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Handle customer.subscription.deleted
|
|
243
|
+
* Downgrade user to free tier
|
|
244
|
+
*/
|
|
245
|
+
async function handleSubscriptionDeleted(pool, subscription) {
|
|
246
|
+
try {
|
|
247
|
+
const customerId = subscription.customer;
|
|
248
|
+
const limits = TIER_LIMITS.free;
|
|
249
|
+
await pool.query(`UPDATE users
|
|
250
|
+
SET
|
|
251
|
+
tier = 'free',
|
|
252
|
+
weekly_limit = $1,
|
|
253
|
+
burst_limit = $2,
|
|
254
|
+
rate_limit = $3,
|
|
255
|
+
stripe_subscription_id = NULL,
|
|
256
|
+
updated_at = now()
|
|
257
|
+
WHERE stripe_customer_id = $4`, [limits.weekly_limit, limits.burst_limit, limits.rate_limit, customerId]);
|
|
258
|
+
log.info(`Subscription deleted for customer ${customerId}: downgraded to free`);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
log.error('Failed to handle subscription deletion', { error: error instanceof Error ? error.message : String(error) });
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Handle invoice.payment_failed
|
|
267
|
+
* Log payment failure (could add email notification here)
|
|
268
|
+
*/
|
|
269
|
+
async function handlePaymentFailed(pool, invoice) {
|
|
270
|
+
try {
|
|
271
|
+
const customerId = invoice.customer;
|
|
272
|
+
// Get user email for logging
|
|
273
|
+
const result = await pool.query('SELECT email FROM users WHERE stripe_customer_id = $1', [customerId]);
|
|
274
|
+
if (result.rows.length > 0) {
|
|
275
|
+
log.warn(`Payment failed for customer ${customerId}`, { email: result.rows[0].email });
|
|
276
|
+
// Note: Email notification not implemented. Log only for now.
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
log.error('Failed to handle payment failure', { error: error instanceof Error ? error.message : String(error) });
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Map Stripe price ID to tier
|
|
286
|
+
* Maps Stripe price IDs to tiers (configured via STRIPE_PRICE_PRO and STRIPE_PRICE_MAX env vars)
|
|
287
|
+
*/
|
|
288
|
+
function getTierFromPriceId(priceId) {
|
|
289
|
+
// Map price IDs to tiers
|
|
290
|
+
const priceMap = {
|
|
291
|
+
// Add your Stripe price IDs here
|
|
292
|
+
[process.env.STRIPE_PRICE_PRO || '']: 'pro',
|
|
293
|
+
[process.env.STRIPE_PRICE_MAX || '']: 'max',
|
|
294
|
+
};
|
|
295
|
+
return priceMap[priceId] || 'free';
|
|
296
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript export endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/transcript/export?url=<youtube_url>&format=srt|txt|md|json
|
|
5
|
+
*
|
|
6
|
+
* Downloads a YouTube transcript in the requested format with appropriate
|
|
7
|
+
* Content-Type and Content-Disposition headers.
|
|
8
|
+
*/
|
|
9
|
+
import { Router } from 'express';
|
|
10
|
+
export declare function createTranscriptExportRouter(): Router;
|