crawlforge-extractors 1.14.0 → 1.15.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/README.md CHANGED
@@ -202,7 +202,7 @@ URL is reported in `reason` as a retired handle.
202
202
  ### Recognising a blocked page
203
203
 
204
204
  `documentVerdict` says what a fetched document is: the page, a bot-wall
205
- interstitial (Cloudflare, Amazon, DataDome, PerimeterX, Akamai, Vercel, AWS WAF, F5), an
205
+ interstitial (Cloudflare, Amazon, DataDome, PerimeterX, Akamai, Vercel, AWS WAF, F5, Fastly), an
206
206
  HTTP error page, an empty shell, a short error-titled placeholder, or a
207
207
  client-rendered app's own short error fallback under a normal title (quora.com's
208
208
  "Something went wrong. Wait a moment and try again."). A wall
package/index.d.ts CHANGED
@@ -308,7 +308,7 @@ export default TemplateRegistry;
308
308
 
309
309
  /** The bot-defence vendor whose interstitial a document is, and what gave it away. */
310
310
  export interface ChallengeVerdict {
311
- vendor: 'cloudflare' | 'amazon' | 'datadome' | 'perimeterx' | 'akamai' | 'vercel' | 'aws-waf' | 'f5';
311
+ vendor: 'cloudflare' | 'amazon' | 'datadome' | 'perimeterx' | 'akamai' | 'vercel' | 'aws-waf' | 'f5' | 'fastly';
312
312
  evidence: string;
313
313
  }
314
314
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crawlforge-extractors",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Extraction logic shared by the CrawlForge MCP server and REST API — scrape templates, charset-correct capped body reading, structural fingerprinting, embedded-state extraction, and query-scoped highlights. One implementation, so the two surfaces cannot drift apart.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * blockedPage.js — decide whether a fetched document is the page or a wall.
3
3
  *
4
- * Cloudflare, Amazon, DataDome, PerimeterX, Akamai, Vercel, AWS WAF and F5 answer
4
+ * Cloudflare, Amazon, DataDome, PerimeterX, Akamai, Vercel, AWS WAF, F5 and Fastly answer
5
5
  * a blocked request with a page of their own, often under HTTP 200: a title, some prose
6
6
  * and a challenge script. Reported as a successful scrape, that page hides
7
7
  * the block — producthunt.com came back "success:true, title: Just a
@@ -80,6 +80,19 @@ const CHALLENGES = [
80
80
  title: /^request rejected$/i,
81
81
  markers: /the requested url was rejected\.?\s*please consult with your administrator/i,
82
82
  evidence: 'an F5 "Request Rejected" blocking page'
83
+ },
84
+ {
85
+ // Fastly's client challenge: HTTP 200, the title "Client Challenge", a
86
+ // <noscript> asking for JavaScript and a loader for
87
+ // /_fs-ch-<token>/script.js, every asset under that same prefix
88
+ // (lemonde.fr to a plain fetch, 3 KB, `via: 1.1 varnish` and
89
+ // `x-served-by: cache-…`, 2026-10-03). Its only text outside <noscript>
90
+ // is a hidden "A required part of this site couldn't load" notice, which
91
+ // is why it read as a page rather than an empty shell.
92
+ vendor: 'fastly',
93
+ title: /^client challenge$/i,
94
+ markers: /\/_fs-ch-[\w-]+\//,
95
+ evidence: 'a Fastly client challenge script'
83
96
  }
84
97
  ];
85
98