SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
12.0 KB · 148 lines tsx
Raw Blame History
1import type { Metadata } from "next";2import Link from "next/link";3import { Badge } from "@/components/ui/badge";4import { Button } from "@/components/ui/button";5import { formatDateOnly } from "@/lib/format";6import { cn } from "@/lib/utils";78export const metadata: Metadata = {9  title: "Changelog",10  description: "Release notes for the Fetcha web access API, dashboard and SDKs. What shipped, what changed, and what is not available yet.",11  alternates: { canonical: "/changelog" },12};1314type ChangeKind = "added" | "changed" | "fixed" | "not-yet";1516type Entry = {17  date: string; // ISO date18  version: string;19  title: string;20  tag?: "preview" | "stable" | "beta";21  summary: string;22  changes: Array<{ kind: ChangeKind; text: string }>;23};2425const ENTRIES: Entry[] = [26  {27    date: "2026-09-08",28    version: "v0.2.1",29    title: "Turnstile captcha solving, real Chrome",30    tag: "stable",31    summary: "The managed browser now solves interactive Cloudflare Turnstile challenges through a human-verification service and runs on a real Google Chrome build.",32    changes: [33      { kind: "added", text: "Captcha solver (2captcha) for Cloudflare Turnstile: widget parameters are intercepted in-page, a token is obtained and injected with the matching user agent. New request field solve_captcha (default true); attempts report captcha_solved in metadata.debug." },34      { kind: "changed", text: "Managed browser runs Google Chrome (stable channel) as a real window on the node instead of the headless Chromium build, with the native user agent and client hints kept coherent." },35      { kind: "changed", text: "Admin → System lists the anti-bot configuration (solver key presence, browser channel and mode); /internal/browser exposes solver statistics." },36    ],37  },38  {39    date: "2026-09-08",40    version: "v0.2.0",41    title: "Private access, managed browser, crawl & map",42    tag: "stable",43    summary: "Fetcha becomes a private, invitation-only platform with a single unlimited plan, and the browser layer goes live: rendered fetches with automatic escalation from plain HTTP, hardened HTTP fingerprints, much broader block detection, Markdown output with page metadata and links, and a new Crawl & Map API with its dashboard page.",44    changes: [45      { kind: "changed", text: "Private access: signup is invitation-only (administrators manage the allowlist and send invitations). One plan, unlimited: no request quota, 200 concurrent requests, 120 s max timeout, 5 retries, all network classes, 90-day retention, browser rendering and crawl jobs. No billing, no checkout." },46      { kind: "added", text: "Managed browser rendering: browser: true renders the page in a headless Chromium on the same network, country and session; wait_for, wait_ms, wait_until, javascript, block_resources and screenshot control the render. 8 concurrent renders per organization; BROWSER_TIMEOUT when a page does not settle." },47      { kind: "added", text: "Automatic escalation: with browser_fallback (default on), an HTTP attempt blocked by a JavaScript challenge is retried in the browser. metadata.mode reports http or browser; debug attempts carry mode and block_reason." },48      { kind: "changed", text: "Hardened HTTP fingerprints: ordered Chrome, Firefox and Safari header profiles rotated per attempt, HTTP/2, Chrome-like TLS cipher ordering, a cookie jar carried across redirects, jittered backoff between retries and Retry-After honoured within the timeout. referer: auto | none | url controls the Referer strategy." },49      { kind: "changed", text: "Expanded block detection: Cloudflare challenge and Turnstile, DataDome, PerimeterX / HUMAN, Akamai, Kasada, Imperva, AWS WAF, Vercel attack mode and soft 200 blocks (challenge, interstitial or empty-shell pages returned with a 200)." },50      { kind: "added", text: "format: \"markdown\" returns the page as Markdown (main content first, boilerplate removed) in markdown. Every HTML response now includes page { title, description, canonical, lang, og, links_count }; links: true adds links[] with absolute URLs, anchor text, internal and nofollow flags." },51      { kind: "added", text: "Crawl API: POST /v1/crawl starts an asynchronous job (max_pages, max_depth, include/exclude patterns, robots, sitemap, concurrency, delay, format, browser); GET /v1/crawl, GET /v1/crawl/:id, GET /v1/crawl/:id/pages (cursor pagination, content per page) and DELETE /v1/crawl/:id. 2,000 pages per job, 5 concurrent jobs. Every page is a normal request in the log with source crawl." },52      { kind: "added", text: "Map API: POST /v1/map lists a site's URLs from its sitemap and links, synchronously, with search filtering." },53      { kind: "added", text: "Dashboard: Crawls page (jobs, status, stats, create dialog, job detail with pages and content preview, cancel, Map tool). Playground: markdown format, live Browser rendering section, Include links, Referer, mode badge, page metadata, Markdown / Links / Screenshot tabs." },54      { kind: "added", text: "SDKs 0.2.0 (JavaScript and Python): markdown format, browser options, links and referer on fetch; crawl.create / get / pages / cancel / wait and map(). User-Agent fetcha-sdk-js/0.2.0 and fetcha-sdk-python/0.2.0." },55      { kind: "changed", text: "BROWSER_UNAVAILABLE is now only returned when the browser pool is disabled or unavailable. New codes CRAWL_NOT_FOUND (404) and CRAWL_LIMIT_REACHED (429)." },56      { kind: "not-yet", text: "Browser actions (POST /v1/browser), structured extraction (/v1/extract), webhook delivery (crawl webhook_url is stored, not called), datacenter, ISP and mobile network classes, teams, OAuth, 2FA, CLI." },57    ],58  },59  {60    date: "2026-09-07",61    version: "v0.1.0",62    title: "Public preview",63    tag: "preview",64    summary: "First public release of Fetcha. One endpoint, automatic routing over the residential network, sticky sessions, and a dashboard to try requests and read logs. Billing checkout was intentionally not part of this release; every account ran on the Free plan (superseded by the single unlimited plan in 0.2.0).",65    changes: [66      { kind: "added", text: "Fetch API: POST /v1/fetch with country, region and city targeting, custom headers, cookies, body, timeout, format (html, text, json, raw), redirect controls and per-request retries." },67      { kind: "added", text: "Automatic routing (network: \"auto\"): weighted per-domain scoring, circuit breakers and cheap-to-premium escalation with a fresh IP on every retry." },68      { kind: "added", text: "Residential network class, live in 45 target countries. auto resolves to residential during the preview." },69      { kind: "added", text: "Sticky sessions: POST /v1/sessions with TTL 60–1,800 s, labels, Idempotency-Key support; list, inspect and close sessions." },70      { kind: "added", text: "Playground in the dashboard: run real requests, inspect attempts and timings, copy generated cURL, JavaScript and Python." },71      { kind: "added", text: "Request logs with per-request id (X-Fetcha-Request-ID), status, network, attempts, latency and bytes. Sensitive headers redacted before storage." },72      { kind: "added", text: "Usage metering: monthly requests and residential bandwidth per project, GET /v1/usage and GET /v1/me." },73      { kind: "added", text: "SDK source for JavaScript/TypeScript (@fetcha/sdk) and Python (fetcha). Install from the repository until the packages are published to npm and PyPI." },74      { kind: "added", text: "Documentation: quickstart, authentication, fetch reference, networks, geolocation, sessions, errors, retries, rate limits, SDKs and examples." },75      { kind: "added", text: "Public status page with 24-hour success rate and 30-day history, and this changelog." },76      { kind: "not-yet", text: "Browser execution (browser: true returns BROWSER_UNAVAILABLE), structured extraction (/v1/extract), datacenter, ISP and mobile network classes." },77      { kind: "not-yet", text: "Billing checkout and self-serve upgrades (email sales@fetcha.co), teams and invitations, webhook delivery, OAuth login, 2FA and passkeys, CLI." },78    ],79  },80];8182const KIND_META: Record<ChangeKind, { label: string; variant: "success" | "info" | "warning" | "outline" }> = {83  added: { label: "Added", variant: "success" },84  changed: { label: "Changed", variant: "info" },85  fixed: { label: "Fixed", variant: "warning" },86  "not-yet": { label: "Not yet", variant: "outline" },87};8889export default function ChangelogPage() {90  return (91    <div className="container-page py-14 sm:py-20">92      <header className="max-w-2xl">93        <p className="text-[11.5px] font-semibold uppercase tracking-[0.12em] text-accent">Changelog</p>94        <h1 className="mt-2 text-[34px] font-semibold leading-[1.1] tracking-tight sm:text-[44px]">What shipped, and what has not.</h1>95        <p className="mt-4 text-[15.5px] leading-relaxed text-fg-muted">96          Release notes for the API, dashboard and SDKs. We list unavailable features explicitly so you never build against something that does not exist yet. Follow the <Link href="/status" className="text-accent underline-offset-4 hover:underline">status page</Link> for incidents. Fetcha is invitation-only: <a href="mailto:hello@fetcha.co?subject=Fetcha%20access" className="text-accent underline-offset-4 hover:underline">request access</a> or <Link href="/login" className="text-accent underline-offset-4 hover:underline">log in</Link>.97        </p>98      </header>99100      <ol className="mt-14 relative" aria-label="Releases">101        <div className="absolute left-[7px] top-2 bottom-2 w-px bg-border sm:left-[151px]" aria-hidden />102        {ENTRIES.map((e) => (103          <li key={e.version} className="relative grid gap-4 pb-16 last:pb-0 sm:grid-cols-[120px_minmax(0,1fr)] sm:gap-12">104            <div className="flex items-center gap-3 pl-6 sm:block sm:pl-0 sm:text-right">105              <time dateTime={e.date} className="font-mono text-[13px] tabular text-fg-muted">106                {formatDateOnly(e.date)}107              </time>108              <span className="absolute left-0 top-[7px] size-[15px] rounded-full border-2 border-accent bg-bg sm:left-[144px]" aria-hidden />109            </div>110            <article className="min-w-0 pl-6 sm:pl-0" aria-labelledby={`rel-${e.version}`}>111              <div className="flex flex-wrap items-center gap-2">112                <Badge variant="solid" className="font-mono">113                  {e.version}114                </Badge>115                {e.tag ? <Badge variant={e.tag === "stable" ? "success" : "accent"}>{e.tag === "preview" ? "Public preview" : e.tag === "beta" ? "Beta" : "Stable"}</Badge> : null}116              </div>117              <h2 id={`rel-${e.version}`} className="mt-3 text-[24px] font-semibold tracking-tight">118                {e.title}119              </h2>120              <p className="mt-2 max-w-2xl text-[14.5px] leading-relaxed text-fg-muted">{e.summary}</p>121              <ul className="mt-6 divide-y divide-border rounded-lg border border-border">122                {e.changes.map((c, i) => (123                  <li key={i} className={cn("flex items-start gap-3 px-4 py-3 text-[14px] leading-relaxed", c.kind === "not-yet" && "bg-bg-subtle/60 text-fg-muted")}>124                    <Badge variant={KIND_META[c.kind].variant} className="mt-0.5 w-[74px] shrink-0 justify-center">125                      {KIND_META[c.kind].label}126                    </Badge>127                    <span className="min-w-0 text-fg [&_code]:font-mono">{c.text}</span>128                  </li>129                ))}130              </ul>131            </article>132          </li>133        ))}134      </ol>135136      <section className="mt-20 flex flex-col items-start justify-between gap-4 rounded-lg border border-border bg-bg-subtle/60 p-6 sm:flex-row sm:items-center">137        <div>138          <h2 className="text-[16px] font-semibold tracking-tight">Missing something you need?</h2>139          <p className="mt-1 text-[13.5px] text-fg-muted">Roadmap priorities follow real usage. Tell us what would unblock you.</p>140        </div>141        <Button asChild variant="outline">142          <a href="mailto:support@fetcha.co?subject=Fetcha%20feature%20request">Send feedback</a>143        </Button>144      </section>145    </div>146  );147}148