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%
3.8 KB · 114 lines typescript
Raw Blame History
1import type { ConcreteNetwork, GeoTarget, HttpMethod } from "@fetcha/core";2import type { CookieJar } from "./cookies";3import type { FingerprintProfile } from "./fingerprint";45export type ProviderId = "oxylabs" | "decodo" | "soax" | "direct";67export interface ProxyEndpoint {8  host: string;9  port: number;10  username: string;11  password: string;12}1314export interface ProviderRequest {15  requestId: string;16  attemptId: string;17  url: string;18  method: HttpMethod;19  headers: Record<string, string>;20  body?: string | Buffer;21  timeoutMs: number;22  network: ConcreteNetwork;23  geo: GeoTarget;24  /** Sticky session key (provider-agnostic). When set the provider keeps the same exit IP. */25  sessionKey?: string | null;26  /** Session lifetime hint in minutes. */27  sessionMinutes?: number;28  followRedirects: boolean;29  maxRedirects: number;30  maxResponseBytes: number;31  /** Called for every redirect hop; must throw to abort. */32  onRedirect?: (nextUrl: string) => Promise<void>;33  /** Browser fingerprint profile to emulate (headers, TLS). Picked by the executor. */34  profile?: FingerprintProfile;35  /** Accept-Language source. */36  locale?: string | null;37  /** Referer to send on the first hop (null/undefined = none). */38  referer?: string | null;39  /** Cookie jar shared across hops (and across requests for sticky sessions). */40  jar?: CookieJar;41  /** Prefer HTTP/2 when the origin supports it (default true). */42  http2?: boolean;43}4445export interface ProviderTiming {46  dns_ms: number;47  proxy_connect_ms: number;48  tls_ms: number;49  origin_ms: number;50  processing_ms: number;51  total_ms: number;52}5354export interface ProviderResponse {55  status: number;56  headers: Record<string, string>;57  body: Buffer;58  finalUrl: string;59  redirects: number;60  bytesIn: number;61  bytesOut: number;62  timing: ProviderTiming;63  /** Provider exit info when known (never exposed to customers). */64  exit?: { ip?: string; country?: string };65  /** Fingerprint profile that was used. */66  profileId?: string;67  /** Negotiated protocol of the final hop when known ("h2" | "http/1.1"). */68  protocol?: string;69}7071export type ProviderHealthStatus = "healthy" | "degraded" | "down" | "unconfigured";7273export interface ProviderHealth {74  provider: ProviderId;75  network: ConcreteNetwork;76  status: ProviderHealthStatus;77  latencyMs: number | null;78  detail?: string;79  checkedAt: Date;80}8182export class ProviderError extends Error {83  readonly provider: ProviderId;84  readonly kind: "timeout" | "connect" | "auth" | "proxy" | "too_large" | "redirect" | "tls" | "unknown";85  readonly status?: number;86  constructor(provider: ProviderId, kind: ProviderError["kind"], message: string, opts: { status?: number; cause?: unknown } = {}) {87    super(message, { cause: opts.cause });88    this.name = "ProviderError";89    this.provider = provider;90    this.kind = kind;91    this.status = opts.status;92  }93}9495export interface ProxyProvider {96  readonly id: ProviderId;97  /** Human label for admin screens. */98  readonly label: string;99  /** Network classes this provider can serve. */100  readonly networks: ConcreteNetwork[];101  /** Whether credentials are present. Unconfigured providers are never routed to. */102  isConfigured(): boolean;103  /** Countries the provider supports; null = worldwide/unknown. */104  supportsGeo(geo: GeoTarget): boolean;105  fetch(request: ProviderRequest): Promise<ProviderResponse>;106  health(network?: ConcreteNetwork): Promise<ProviderHealth>;107  /** Estimated upstream cost in USD for a request transferring `bytes`. */108  estimateCost(network: ConcreteNetwork, bytes: number): number;109  /** Unit price in USD per GB for a network class. */110  pricePerGb(network: ConcreteNetwork): number;111  /** Upstream proxy endpoint for a request (null = direct egress). Used by the managed browser. */112  proxyEndpoint(req: Pick<ProviderRequest, "geo" | "sessionKey" | "sessionMinutes">): ProxyEndpoint | null;113}114