import { A as AutomationAssessment } from './version-Cv9x48z9.js'; export { a as AutomationConfidence, b as AutomationKind, V as VERSION, i as isBotUserAgent } from './version-Cv9x48z9.js'; interface TlsFingerprintEntry { id: string; label: string; /** Defaults to `ja3` for backward compatibility. */ fingerprintType?: "ja3" | "ja4"; hash?: string; prefix?: string; families: UserAgentFamily[]; confidence: ConfidenceLevel; } type UserAgentFamily = "chrome" | "chrome-headless" | "edge" | "firefox" | "safari" | "curl" | "python" | "go" | "java" | "scripting" | "unknown"; /** Curated automation and scripting TLS fingerprints (JA3 hash or prefix) */ declare const KNOWN_SUSPICIOUS_TLS_FINGERPRINTS: TlsFingerprintEntry[]; /** * Whether a string is a well-formed JA3 hash (32-character lowercase MD5). * A hash of any other length can never match a real JA3 and is almost always * a typo or a truncated/expanded copy-paste. */ declare function isValidJa3Hash(value: string): boolean; /** Lowercases and trims a JA3/JA4 fingerprint for comparison. */ declare function normalizeTlsFingerprint(fingerprint: string): string; /** Coarse client family from a User-Agent string (`chrome`, `curl`, `python`, …). */ declare function getUserAgentFamily(userAgent: string | undefined): UserAgentFamily; /** Whether the User-Agent claims to be a real browser (Chrome/Edge/Firefox/Safari). */ declare function isBrowserLikeUserAgent(userAgent: string | undefined): boolean; /** Finds the curated (or caller-supplied) entry matching a TLS fingerprint. */ declare function findTlsFingerprintEntry(fingerprint: string, extraFingerprints?: string[], fingerprintType?: "ja3" | "ja4", extraEntries?: TlsFingerprintEntry[]): TlsFingerprintEntry | undefined; /** Whether the fingerprint matches a known automation/scripting TLS client. */ declare function isKnownSuspiciousTlsFingerprint(fingerprint: string | undefined, extraFingerprints?: string[], fingerprintType?: "ja3" | "ja4", extraEntries?: TlsFingerprintEntry[]): boolean; /** * Whether a recognized TLS fingerprint contradicts the declared User-Agent — * e.g. a curl JA3 hash presented alongside a Chrome UA. */ declare function isTlsUserAgentMismatch(fingerprint: string | undefined, userAgent: string | undefined, extraFingerprints?: string[], fingerprintType?: "ja3" | "ja4", extraEntries?: TlsFingerprintEntry[]): boolean; /** Browser-like UA arriving without a TLS fingerprint (only when `requireTlsFingerprint`). */ declare function isMissingTlsFingerprint(fingerprint: string | undefined, userAgent: string | undefined, requireTlsFingerprint: boolean): boolean; type ConfidenceLevel = "high" | "medium" | "low"; /** Result of crawler identity verification performed by trusted edge infrastructure. */ type CrawlerVerificationStatus = "verified" | "spoofed" | "unverified"; interface ServerSignal { id: string; description: string; triggered: boolean; weight: number; confidence: ConfidenceLevel; score: number; } interface ServerClientContext { /** Client IP address — enables GeoIP lookup and blocklist checks when set */ clientIp?: string; /** IANA timezone from GeoIP lookup of the client IP (e.g. `America/New_York`) */ ipTimezone?: string; /** Timezone reported by the client via header, cookie, or JS beacon */ clientTimezone?: string; /** JA3/JA4 hash or raw JA3 string from the TLS terminator */ tlsFingerprint?: string; /** Defaults to `ja3` */ tlsFingerprintType?: "ja3" | "ja4"; userAgent?: string; /** User-Agent reported by browser JavaScript, for header/realm comparison */ clientUserAgent?: string; acceptLanguage?: string; /** navigator.language reported by a client beacon */ clientLanguage?: string; /** navigator.languages reported by a client beacon */ clientLanguages?: string[]; /** navigator.platform reported by a client beacon */ clientPlatform?: string; /** Chromium User-Agent Client Hints header (`sec-ch-ua`) */ secChUa?: string; /** Chromium `sec-ch-ua-platform` header */ secChUaPlatform?: string; /** Chromium `sec-ch-ua-mobile` header (`?0` or `?1`) */ secChUaMobile?: string; secFetchSite?: string; secFetchMode?: string; secFetchDest?: string; /** ISO 3166-1 alpha-2 country code from GeoIP */ ipCountry?: string; /** Whether the IP is a known datacenter/hosting range (auto-detected or manual) */ isDatacenterIp?: boolean; /** Whether the IP appears on the bundled AbuseIPDB 30-day blocklist */ isAbuseListedIp?: boolean; /** Whether the IP is in Apple's iCloud Private Relay egress ranges */ isIcloudPrivateRelay?: boolean; /** * Trusted-edge verdict for Web Bot Auth, reverse/forward DNS, or published * crawler IP-range verification. Use `spoofed` only when infrastructure can * distinguish a claimed identity and conclusively reject it; ambiguous auth * failures are `unverified`. Only `spoofed` adds suspicion. */ crawlerVerificationStatus?: CrawlerVerificationStatus; } interface ServerDetectorOptions { /** Offset tolerance in minutes when comparing IP vs client timezone */ timezoneToleranceMinutes?: number; /** Suspicion score threshold below which `isLegitClient` is true */ scoreThreshold?: number; /** Additional JA3/JA4 hashes or raw fingerprint prefixes to treat as suspicious */ suspiciousTlsFingerprints?: string[]; /** Family-labelled JA3/JA4 entries used for TLS/UA consistency checks. */ suspiciousTlsFingerprintEntries?: TlsFingerprintEntry[]; /** When true, flags browser-like user agents that omit a TLS fingerprint */ requireTlsFingerprint?: boolean; /** When true, flags browser UAs missing the standard Sec-Fetch metadata headers. */ requireBrowserHeaders?: boolean; /** Directory containing `data/*.csv` blocklists (defaults to package `data/`) */ dataDir?: string; /** When false, skips `doc999tor-fast-geoip` lookup for `clientIp` */ lookupGeo?: boolean; /** When false, skips bundled abuse/datacenter/iCloud relay list checks */ checkIpLists?: boolean; } interface ServerClientResult { suspicionScore: number; confidence: ConfidenceLevel; signals: ServerSignal[]; isLegitClient: boolean; /** Best-effort request-client attribution with evidence and alternatives. */ automation: AutomationAssessment; context: { clientIp?: string; ipTimezone?: string; clientTimezone?: string; tlsFingerprint?: string; tlsFingerprintType?: "ja3" | "ja4"; userAgent?: string; clientUserAgent?: string; acceptLanguage?: string; clientLanguage?: string; clientLanguages?: string[]; clientPlatform?: string; secChUa?: string; secChUaPlatform?: string; secChUaMobile?: string; secFetchSite?: string; secFetchMode?: string; secFetchDest?: string; ipCountry?: string; isDatacenterIp?: boolean; isAbuseListedIp?: boolean; isIcloudPrivateRelay?: boolean; crawlerVerificationStatus?: CrawlerVerificationStatus; datacenterProvider?: string; icloudRelayCountry?: string; }; } /** * Explicit scripting-library User-Agent (curl, Python, Go, or Java). * @internal */ declare function isScriptingUserAgent(userAgent: string | undefined): boolean; /** * Chromium UA version disagrees with the `sec-ch-ua` brand/version header. * @internal */ declare function isClientHintsMismatch(userAgent: string | undefined, secChUa: string | undefined): boolean; /** HTTP User-Agent disagrees with the value observed by browser JavaScript. */ declare function isClientUserAgentMismatch(userAgent: string | undefined, clientUserAgent: string | undefined): boolean; /** Accept-Language's preferred value disagrees with Navigator language data. */ declare function isClientLanguageMismatch(acceptLanguage: string | undefined, clientLanguage: string | undefined, clientLanguages: string[] | undefined): boolean; /** UA operating-system claim conflicts with JS platform or Client Hints. */ declare function isClientPlatformMismatch(userAgent: string | undefined, clientPlatform: string | undefined, secChUaPlatform: string | undefined): boolean; /** sec-ch-ua-mobile conflicts with the User-Agent's mobile claim. */ declare function isClientHintsMobileMismatch(userAgent: string | undefined, secChUaMobile: string | undefined): boolean; /** * Browser UA missing one or more Fetch Metadata headers, when explicitly required. * @internal */ declare function isMissingBrowserHeaders(context: ServerClientContext, requireBrowserHeaders: boolean): boolean; /** * Evaluates every server-side heuristic and returns the weighted signal list. * @internal */ declare function buildServerSignals(context: ServerClientContext, options?: ServerDetectorOptions): ServerSignal[]; interface EnrichedServerContext extends ServerClientContext { datacenterProvider?: string; icloudRelayCountry?: string; } /** * Fills missing context fields from `clientIp`: GeoIP country/timezone * (unless `lookupGeo: false`) and bundled blocklist matches (unless * `checkIpLists: false`). Fields already present are never overwritten. */ declare function enrichServerContext(context: ServerClientContext, options?: ServerDetectorOptions): Promise; interface IpGeoResult { ipCountry?: string; ipTimezone?: string; city?: string; region?: string; } /** * Looks up country, timezone, city, and region for an IP using the bundled * offline GeoIP database (`doc999tor-fast-geoip`). Returns `null` when the IP * is unknown or invalid — never throws. */ declare function lookupClientIpGeo(ip: string): Promise; /** Result of matching one IP against the bundled blocklists. */ interface IpListMatchResult { /** IP falls inside a known datacenter/hosting provider range */ isDatacenterIp: boolean; /** IP appears on the AbuseIPDB 30-day blocklist */ isAbuseListedIp: boolean; /** IP is an iCloud Private Relay egress address */ isIcloudPrivateRelay: boolean; /** Provider name when `isDatacenterIp` is true (e.g. `Amazon AWS`) */ datacenterProvider?: string; /** ISO country code of the relay egress when `isIcloudPrivateRelay` is true */ icloudRelayCountry?: string; } /** Pre-parsed blocklist matcher. Create once and reuse — see {@link getIpListChecker}. */ interface IpListChecker { /** Match an IPv4 or IPv6 address against all bundled lists. Invalid input matches nothing. */ check(ip: string): IpListMatchResult; /** Entry counts loaded from the data directory. */ stats: { abuseIpCount: number; datacenterRangeCount: number; icloudRelayRangeCount: number; }; } type ParsedIp = { kind: "ipv4"; value: number; canonical: string; } | { kind: "ipv6"; value: bigint; canonical: string; }; /** * Parses an IPv4 or IPv6 address. IPv4-mapped IPv6 addresses (`::ffff:1.2.3.4`) * normalize to IPv4 so proxies handing over mapped addresses still match. * Returns `null` for anything that is not a valid address. */ declare function parseIp(address: string): ParsedIp | null; /** * Builds a blocklist matcher from the CSVs in `dataDir` (defaults to the * package's bundled `data/`). Parsing happens once here; `check` calls are * then O(log n) binary searches. Prefer {@link getIpListChecker}, which caches * the parsed lists per data directory. */ declare function createIpListChecker(dataDir?: string): IpListChecker; /** * Returns a cached {@link IpListChecker} for `dataDir`, creating it on first use. * * Note: Because of how ESM/CJS interop works, different module instances * (e.g. a mix of import vs require, or multiple copies of the package) * may each have their own cache. For most server apps this is fine. * Call `preloadIpLists()` early at boot. */ declare function getIpListChecker(dataDir?: string): IpListChecker; /** * Eagerly parses the bundled IP lists into the shared cache so the first * request-time check doesn't pay the one-off parse cost (~0.5s for the full * bundled data). Call once at server boot. Returns the loaded entry counts. * * See {@link getIpListChecker} for notes on module caching across ESM/CJS. */ declare function preloadIpLists(dataDir?: string): IpListChecker["stats"]; /** * Clears the cached checker — used by tests that swap data directories. * @internal */ declare function resetIpListCheckerCache(): void; /** * Absolute path of the package's bundled `data/` directory. * @internal */ declare function getDefaultIpDataDir(): string; /** * Combines triggered signal weights into one score: * `1 - Π(1 - weightᵢ)` — independent-probability union, so extra signals * always raise the score but never past 1. * @internal */ declare function aggregateServerSuspicionScore(signals: ServerSignal[]): number; /** * Confidence in the verdict based on high-confidence signal hits and score. * @internal */ declare function resolveServerConfidence(signals: ServerSignal[], suspicionScore: number): ConfidenceLevel; /** * Scores an already-enriched request context synchronously. Use this when you * have your own GeoIP/blocklist pipeline; otherwise prefer * {@link detectServerClientAsync}, which fills the context from `clientIp`. */ declare function detectServerClient(context: ServerClientContext, options?: ServerDetectorOptions): ServerClientResult; /** * Scores a request in one call. When `clientIp` is set, the context is * auto-enriched with GeoIP country/timezone and bundled blocklist matches * (datacenter ranges, AbuseIPDB, iCloud Private Relay) before scoring. * * @example * const result = await detectServerClientAsync({ * clientIp: req.ip, * clientTimezone: req.headers["x-timezone"], * userAgent: req.headers["user-agent"], * tlsFingerprint: req.headers["x-ja3-hash"], * }); * if (!result.isLegitClient) return res.status(403).end(); */ declare function detectServerClientAsync(context: ServerClientContext, options?: ServerDetectorOptions): Promise; /** Current UTC offset in minutes for an IANA timezone, or `null` when unknown. */ declare function getTimezoneOffsetMinutes(timeZone: string, at?: Date): number | null; /** * Whether the client-reported timezone disagrees with the GeoIP timezone by * more than `toleranceMinutes` of UTC offset. Different zone names with the * same offset (e.g. `Europe/Paris` vs `Europe/Berlin`) do not mismatch. */ declare function isTimezoneMismatch(ipTimezone: string | undefined, clientTimezone: string | undefined, toleranceMinutes?: number): boolean; /** * Whether no Accept-Language alpha-2 country region matches the GeoIP country. * Region-less headers (plain `en`) and acceptable wildcards never mismatch. */ declare function isAcceptLanguageGeoMismatch(acceptLanguage: string | undefined, ipCountry: string | undefined): boolean; /** Datacenter/hosting IP presenting a residential browser User-Agent. */ declare function isDatacenterBrowserMismatch(isDatacenterIp: boolean | undefined, userAgent: string | undefined): boolean; export { AutomationAssessment, type CrawlerVerificationStatus, type EnrichedServerContext, type IpGeoResult, type IpListChecker, type IpListMatchResult, KNOWN_SUSPICIOUS_TLS_FINGERPRINTS, type ParsedIp, type ServerClientContext, type ServerClientResult, type ConfidenceLevel as ServerConfidenceLevel, type ServerDetectorOptions, type ServerSignal, type TlsFingerprintEntry, type UserAgentFamily, aggregateServerSuspicionScore, buildServerSignals, createIpListChecker, detectServerClient, detectServerClientAsync, enrichServerContext, findTlsFingerprintEntry, getDefaultIpDataDir, getIpListChecker, getTimezoneOffsetMinutes, getUserAgentFamily, isAcceptLanguageGeoMismatch, isBrowserLikeUserAgent, isClientHintsMismatch, isClientHintsMobileMismatch, isClientLanguageMismatch, isClientPlatformMismatch, isClientUserAgentMismatch, isDatacenterBrowserMismatch, isKnownSuspiciousTlsFingerprint, isMissingBrowserHeaders, isMissingTlsFingerprint, isScriptingUserAgent, isTimezoneMismatch, isTlsUserAgentMismatch, isValidJa3Hash, lookupClientIpGeo, normalizeTlsFingerprint, parseIp, preloadIpLists, resetIpListCheckerCache, resolveServerConfidence };