/** * Guarded fetch helpers and SSRF-safe origin construction for source-sync. * * Owns: source-sync-specific fetch wrappers, allowed-origin derivation from * checked-in source endpoints, URL normalization, sitemap/HTML link extraction, * and deduplication. The actual public-hostname / IP SSRF backstop lives in * lib/http.ts; this module only builds per-source origin allowlists from * checked-in source definitions. * * SECURITY NOTE — getAllowedOrigins(): * Origins are derived from checked-in source definitions whose URLs are * authored by source pack maintainers, not from arbitrary user-provided * runtime input. The guarded fetch layer still enforces public-hostname and * IP-range checks. Do NOT reuse this self-derived allowlist pattern for * arbitrary user-provided runtime endpoints. */ import type { SourceSyncFetchOptions } from "./types.js"; /** Maximum bytes read from a standard source-sync HTTP response. */ export declare const SOURCE_SYNC_FETCH_MAX_BYTES = 5000000; /** Maximum bytes read from a large-response source (e.g. full NuGet catalog page). */ export declare const SOURCE_SYNC_LARGE_RESPONSE_MAX_BYTES = 25000000; /** Number of entries fetched per paginated request (per_page / limit / rows / take) * across all registries, and the batch window during source-sync import. */ export declare const SOURCE_SYNC_BATCH_SIZE = 50; /** * Maximum number of catalog entries a single package-registry source may * contribute to the indexed catalog. Aligns the Packagist (PHP) full-snapshot * source with the paginated registries (pypi: 500, cargo: 500, nuget: 500). * Prevents any single registry from dominating catalog composition and * degrading selection rates for unrelated workspaces. */ export declare const SOURCE_SYNC_INDEXED_REGISTRY_ENTRY_CAP = 500; /** Default HTTP request timeout in milliseconds for source-sync fetches. */ export declare const SOURCE_SYNC_TIMEOUT_MS = 30000; /** Default HTTP headers sent with every source-sync request. */ export declare const SOURCE_SYNC_HEADERS: { Accept: string; "User-Agent": string; }; /** Maximum retry attempts for transient source-sync fetch failures * (fetchWithRetry performs 1 initial attempt + up to maxRetries retries). */ export declare const SOURCE_SYNC_MAX_RETRIES = 3; /** Base backoff delay in ms for source-sync retries (exponential: delay × 2ⁿ). */ export declare const SOURCE_SYNC_RETRY_BASE_DELAY_MS = 1000; /** * Error wrapper for failures where retrying is pointless (SSRF rejections, * 4xx client errors, invalid URLs, policy blocks). Callers can use * `instanceof NonTransientFetchError` for reliable error discrimination * instead of fragile string-matching on error messages. */ export declare class NonTransientFetchError extends Error { constructor(message: string, cause?: unknown); } /** * Returns true when `err` is a NonTransientFetchError (explicit marker) * or has a known non-transient HTTP status (4xx range). */ export declare function isNonTransientError(err: unknown): boolean; /** * Type guard: returns true when `err` has a numeric HTTP `status` property. */ export declare function hasHttpStatus(err: Error): err is Error & { status: number; }; /** * Executes `fetchFn`, retrying on transient failures with exponential backoff. * * Non-transient errors (NonTransientFetchError, HTTP 4xx) are rethrown * immediately without retry. Transient errors retry up to `maxRetries` times * with delays of `baseDelayMs × 2ⁿ`. */ export declare function fetchWithRetry(url: string, fetchFn: () => Promise, options?: SourceSyncFetchOptions): Promise; /** * Fetches the URL as plain text with retry-on-transient-failure. * Throws when all retry attempts are exhausted or the request hits a * non-transient failure. */ export declare function fetchRequiredText(url: string, allowedOrigins: readonly string[], options?: SourceSyncFetchOptions): Promise; /** * Describes an HTTP request override for JSON fetches: the method, a * serialized body, and headers merged over the standard source-sync headers. */ export interface SourceSyncJsonRequest { /** HTTP method; left undefined when no body is present, defaults to POST when a body is provided. */ method?: "GET" | "POST"; /** Serialized request body (e.g. a JSON search payload). */ body?: string; /** Extra headers merged over the standard source-sync headers (any HeadersInit form). */ headers?: HeadersInit; } /** * Fetches the URL as parsed JSON with retry-on-transient-failure. * Throws when all retry attempts are exhausted or the request hits a * non-transient failure. */ export declare function fetchRequiredJson(url: string, allowedOrigins: readonly string[], options?: SourceSyncFetchOptions, request?: SourceSyncJsonRequest): Promise; /** * Builds source-sync fetch origins from checked-in source definitions. Source * pack authors are trusted to point at legitimate registry/docs endpoints, while * guarded fetch still enforces the public-hostname/IP SSRF backstop. Do not * reuse this self-derived allowlist pattern for arbitrary user-provided runtime * endpoints. */ export declare function getAllowedOrigins(...urls: Array): string[]; /** * Extracts the URL origin (scheme + host + port) from a URL string. * Returns an empty array when the URL is undefined or unparseable. */ export declare function getAllowedOrigin(url: string | undefined): string[]; /** * Resolves `rawUrl` relative to `baseUrl`, strips query and hash, and returns * the result in an array. Returns an empty array when the URL is unparseable. */ export declare function toSameOriginUrl(rawUrl: string, baseUrl: string): URL[]; /** * Returns a copy of `url` with query string and hash fragment removed. */ export declare function stripUrlQueryAndHash(url: URL): URL; /** * Returns true when `url.origin` is present in `allowedOrigins` (case-insensitive). */ export declare function isAllowedOriginUrl(url: URL, allowedOrigins: readonly string[]): boolean; /** * Deduplicates a URL array by string identity, preserving first-seen order. */ export declare function dedupeUrls(urls: URL[]): URL[]; /** * Fetches the root sitemap index, resolves leaf sitemap URLs that pass the * optional predicate, and returns them sorted. Falls back to the root URL when * the index contains no `` entries (i.e. the root is itself a urlset). */ export declare function resolveSitemapLeafUrls(rootSitemapUrl: string, allowedOrigins: readonly string[], leafSitemapPredicate?: (url: URL) => boolean): Promise; /** * Parses `…` entries from a sitemap index XML * document and returns them as resolved URL objects. */ export declare function parseSitemapIndex(content: string, baseUrl: string): URL[]; /** * Parses `…` entries from a sitemap urlset XML document * and returns them as resolved URL objects. */ export declare function parseUrlSet(content: string, baseUrl: string): URL[]; /** * Extracts all href/path matches of `pattern` from `content`, resolves them * relative to `baseUrl`, filters to `allowedOrigins`, strips query/hash, and * deduplicates the result. */ export declare function extractNormalizedLinks(content: string, baseUrl: string, allowedOrigins: readonly string[], pattern: RegExp): URL[]; /** * Casts `value` to a plain object record. Returns an empty object when the * value is not a non-null, non-array object. */ export declare function asRecord(value: unknown): Record; /** * Returns true when `value` is a non-null, non-array plain object. */ export declare function isRecord(value: unknown): value is Record; /** * Returns `value` as a string when it is a non-empty string; undefined otherwise. */ export declare function getString(value: unknown): string | undefined; /** * Returns `value` as a number when it is a finite number; undefined otherwise. */ export declare function getNumber(value: unknown): number | undefined; /** * Coerces `value` to a string when it is a string or finite number; returns * undefined for all other types. */ export declare function stringifyUnknown(value: unknown): string | undefined; /** * Filters an array-like value to string elements. Returns an empty array when * the value is not an array. */ export declare function normalizeStringArray(value: unknown): string[]; /** * Extracts the `.message` property from an `Error`, or coerces `error` to a * string for non-Error values. */ export declare function getErrorMessage(error: unknown): string;