import type { ActionContext } from '../actions.js';
import { type TeamOptions } from '../auth/team.js';
import type { CollectionDefinition } from '../config/types.js';
import type { FetchFormEntriesParams, FetchFormsParams, FormDefinition } from '../forms/types.js';
import { type IncomingFile } from '../forms/files.js';
import { type ApiKeyScope } from '../security/api-key-enhanced.js';
export declare function importBlobStorage(): Promise<any>;
export declare function importAIPlugin(): Promise<any>;
/**
 * Build the system/user prompt for an inline co-author action. Kept
 * deterministic and small so the governed runtime can route it to any
 * configured provider/model.
 */
export declare function buildCoauthorPrompt(action: 'rewrite' | 'expand' | 'compress' | 'proofread', body: {
    text?: string;
    style?: string;
    tone?: string;
    instructions?: string;
    targetLength?: number;
}): {
    system: string;
    user: string;
    maxTokens: number;
};
/**
 * Common helper for the read endpoints. Reads the `?populate=` query param,
 * fetches the target collection's field schema, and expands every relation
 * reference into a {@link RelationSummary}. When the caller didn't ask for
 * population (or the doc has no `data` field) we short-circuit and return
 * the doc untouched.
 *
 * Lives here (not in `actions.ts`) because the rest of the actions API
 * stays purely DB-shaped — population is an HTTP concern.
 */
export declare function maybePopulate(request: Request, collection: string, doc: Record<string, unknown> | null, dbInstance: unknown): Promise<Record<string, unknown> | null>;
export declare const SECURITY_HEADERS: Record<string, string>;
export declare function json(data: unknown, status?: number): Response;
/**
 * Variant of `json()` that emits the Phase-5.5 public-read
 * `Cache-Control` header when the active CMS config opts in.
 *
 * - Session-cookie requests always get `private, no-store`.
 * - API-key / unauthenticated requests get
 *   `public, s-maxage=N, stale-while-revalidate=M` when
 *   `cache.publicReads.enabled` is true.
 * - Otherwise no `Cache-Control` header is set (i.e. the response is
 *   uncacheable downstream, matching the pre-5.5 behaviour).
 *
 * Use this for the public read endpoints (`/collections/:slug`,
 * `/collections/:slug/:id`, `/globals/:slug`, `/resolve`). Mutating
 * endpoints stay on the plain `json()` so the CDN never caches a write
 * response by accident.
 */
export declare function jsonWithCache(data: unknown, request: Request, status?: number): Response;
export declare function errorResponse(message: string, status: number): Response;
export declare function internalError(err: unknown, context?: string): Response;
export declare function clampPageSize(raw: number | string | null, max?: number, fallback?: number): number;
export declare function mediaUrl(storageKey: unknown): string;
export declare function normalizeMediaItem(media: any): any;
/**
 * Build the admin editor route for a document that references a media asset.
 * Pages own the `/pages/*` namespace; post-type collections use
 * `/posts/:type/:id/edit`; everything else falls back to the generic
 * collection editor. Kept in sync with the route table in `AdminRoot.tsx`.
 */
export declare function mediaUsageEditPath(collection: string, id: string): string;
/**
 * Map the `MediaUsage` join rows (with their related document) into the
 * `{ page, path }[]` shape the admin drawer renders. Soft-deleted documents
 * are excluded so trashed pages don't keep an asset looking "in use".
 */
export declare function buildMediaUsedOn(mediaUsages: unknown): {
    page: string;
    path: string;
}[];
export declare const MEDIA_TITLE_STOPWORDS: Set<string>;
/**
 * Derive a concise, Title-Cased title from AI-generated alt text. The alt
 * text is already a model output describing the image, so the title stays
 * grounded in real content rather than the filename.
 */
export declare function deriveMediaTitleFromAlt(alt: string): string;
export declare function asRecord(value: unknown): Record<string, unknown>;
export declare function normalizeSubmission(submission: any): any;
/**
 * Map a forms-service domain error to an HTTP response. Returns `null` for
 * unrecognised errors so the caller can fall through to `internalError`.
 */
export declare function mapFormError(err: unknown): Response | null;
export declare function parseFormsQuery(url: URL): FetchFormsParams;
export declare function parseEntriesQuery(url: URL): FetchFormEntriesParams;
/**
 * Build CORS headers for the public form endpoints. When the form has no
 * embed allowlist we reflect the requesting origin (open embedding). When an
 * allowlist is set we only echo the origin if it matches, so a disallowed site
 * cannot read the response. Browser-level CORS is complemented by a hard
 * server-side origin check in the submit handler.
 */
export declare function formCorsHeaders(request: Request, allowedDomains?: string[]): Record<string, string>;
export declare function normalizeAllowedHost(domain: string): string;
export declare function isOriginAllowed(origin: string, allowedDomains: string[]): boolean;
export declare function jsonWithHeaders(data: unknown, status: number, extra: Record<string, string>): Response;
/**
 * Parse a public submission request into field values, files, and metadata.
 * Supports both `application/json` (no files) and `multipart/form-data`
 * (files + fields). Unknown/oversized bodies are rejected by the caller's
 * Content-Length gate before this runs.
 */
export declare function parseSubmissionRequest(request: Request): Promise<{
    values: Record<string, unknown>;
    files: IncomingFile[];
    meta: {
        attribution: Record<string, unknown> | null;
        elapsedMs: number | null;
        pageUrl: string | null;
        captchaToken: string;
    };
} | null>;
export declare function safeJsonObject(value: unknown): Record<string, unknown> | null;
/**
 * Shared public-submission handler used by both the slug-based public route and
 * the legacy by-id route. Enforces per-IP+form rate limiting, embed-origin
 * allowlisting, optional CAPTCHA, file validation, then runs the core
 * submission pipeline. Always responds with CORS headers so embedded forms can
 * read the result.
 */
export declare function submitToForm(request: Request, form: FormDefinition): Promise<Response>;
export declare function normalizeRedirect(redirect: any): any;
export declare function normalizeSeoPage(page: any): any;
export declare function hasModel(d: any, name: string): boolean;
/** Format a number compactly, e.g. 8400 -> "8.4K", 1_200_000 -> "1.2M". */
export declare function formatCompactNumber(n: number): string;
/** Minimal CSV line parser supporting double-quoted fields with escaped quotes. */
export declare function parseCsvLine(line: string): string[];
export declare function modelNotAvailable(name: string): Response;
/**
 * XML-escape a value for safe inclusion in sitemap content. Sitemaps reject
 * unescaped ampersands and angle brackets; URLs frequently contain `&` query
 * separators, so this is non-optional.
 */
export declare function escapeXml(value: string): string;
/**
 * URLs per sitemap file. The sitemaps protocol caps a single file at 50,000
 * URLs; we use a margin below that so the optional archive URL on page 0 can
 * never push a file over the limit. Collections larger than this are split
 * across multiple `?page=N` files referenced from the sitemap index.
 */
export declare const SITEMAP_PAGE_SIZE = 45000;
/**
 * Renders a 1200x630 SVG suitable for og:image. We don't pull in Satori/resvg
 * because most integrators don't need PNG — major crawlers (Facebook, Twitter,
 * LinkedIn, Slack, Discord) handle image/svg+xml correctly. Sites that
 * specifically need PNG can override with their own /og endpoint via
 * @vercel/og and point `seo.defaultOgImage` at it.
 */
export declare function renderOgSvg(opts: {
    title: string;
    description?: string;
    siteName?: string | null;
    theme: 'light' | 'dark';
    bg: string;
    fg: string;
    muted: string;
}): string;
/**
 * Log unexpected query failures from the `safe*` wrappers.
 *
 * The wrappers exist so an optional Prisma model (e.g. a delegate
 * that older schemas don't define) doesn't blow up the route — they
 * return the empty value (`0` / `[]`) when the model isn't available.
 * That defensive contract historically swallowed *every* error,
 * including Prisma's "Unknown argument" rejections that flag a real
 * application bug (see the `relationLoadStrategy` regression
 * discovered while landing Phase 5.5 #5). Logging the underlying
 * error preserves the defensive return semantics while making the
 * actual class of bug visible in Vercel + Sentry logs.
 *
 * Kept private to this module — it's a logging contract, not a
 * public API.
 */
export declare function logSafeQueryFailure(scope: string, err: unknown): void;
export declare function safeCount(model: any, where?: Record<string, unknown>): Promise<number>;
export declare function safeFindMany<T>(model: any, args: Record<string, unknown>): Promise<T[]>;
export declare function isAllowedStorageUrl(url: string): boolean;
/** Max image size (bytes) we'll pull into memory for vision analysis. */
export declare const MAX_VISION_IMAGE_BYTES: number;
/**
 * Fetch image bytes for multimodal (vision) analysis through {@link safeFetch}
 * (SSRF-safe: re-validates the host, DNS-resolves to defeat rebinding, no
 * redirects). Returns `null` on any failure (non-image, oversize, blocked,
 * network) so the caller transparently falls back to a context-only heuristic.
 */
export declare function fetchImageForVision(url: string, knownMimeType?: string): Promise<{
    base64: string;
    mimeType: string;
} | null>;
export declare const ALLOWED_SORT_FIELDS: Set<string>;
export declare function getSessionSecret(): string;
export declare function isSecretMissing(): boolean;
/**
 * Authenticated request context. `apiKey` is set when the request was
 * authenticated via an `act_sk_*` API key (rather than a user session) — in
 * that case `sessionId` is the API key id and `role` reflects either the
 * granted `admin` scope or `'API_KEY'` for scoped keys.
 */
export type AuthSession = {
    userId: string;
    role: string;
    sessionId: string;
    apiKey?: {
        id: string;
        scopes: ApiKeyScope;
    };
};
export declare function extractSession(request: Request): Promise<AuthSession | null>;
export declare function parseCookieHeader(cookieHeader: string): Record<string, string>;
/**
 * Verify a password (or "current password") posted in the X-Reauth-Password
 * header against the authenticated user. Returns an error Response when the
 * header is missing or the password is wrong; returns null on success.
 *
 * Use this for high-impact account changes (TOTP enable/disable, role change,
 * delete user, etc.) so a stolen session alone cannot perform them.
 */
export declare function requirePasswordReauth(request: Request, userId: string): Promise<Response | null>;
export declare function requireAuth(request: Request): Promise<{
    session: AuthSession;
    error?: never;
} | {
    session?: never;
    error: Response;
}>;
/**
 * Check that the request's auth context permits `action` on `collection`.
 * - Session-authenticated requests fall through to `requireRole` semantics:
 *   write actions require WRITE_ROLES.
 * - API-key-authenticated requests must have an explicit scope match.
 */
export declare function requireCollectionScope(session: AuthSession, collection: string, action: 'read' | 'create' | 'update' | 'delete'): Response | null;
export declare function requireGlobalScope(session: AuthSession, slug: string): Response | null;
export declare function requireMediaScope(session: AuthSession): Response | null;
export declare function requirePageBuilderScope(session: AuthSession): Response | null;
export declare function requireAdminScope(session: AuthSession): Response | null;
export declare function buildActionContext(session: {
    userId: string;
    role: string;
}, db: any, locale?: string): ActionContext;
export declare const WRITE_ROLES: Set<string>;
export declare const ADMIN_ROLES: Set<string>;
export declare function requireRole(role: string, allowedRoles: Set<string>): Response | null;
/**
 * Build the shared {@link TeamOptions} for the Users endpoints from config +
 * session: the workspace domain (for external-domain detection) and the seat
 * cap (config-provided; null = unlimited until billing exists).
 */
export declare function teamOptionsFor(session: AuthSession): TeamOptions;
/** Whether the configured email provider can actually send mail. */
export declare function isEmailConfigured(): boolean;
export declare const loginLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const inviteLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const totpLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const formLimiterGlobal: import("../security/rate-limit.js").RateLimiter;
export declare const aiGenerateLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const uploadLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const aiAnalysisLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const AI_ANALYSIS_MAX_CHARS = 50000;
export declare const linkHealthLimiter: import("../security/rate-limit.js").RateLimiter;
export declare const seoAuditLimiter: import("../security/rate-limit.js").RateLimiter;
export declare function checkRateLimitAsync(limiter: {
    check: (key: string) => Promise<{
        allowed: boolean;
    }>;
}, key: string): Promise<boolean>;
/**
 * Resolve the client IP from trusted-proxy headers (Vercel first, then x-real-ip,
 * then x-forwarded-for only when ACTUATE_TRUST_PROXY=1). Returns 'unknown' when
 * no trustworthy source is available — callers MUST treat that case as a hard
 * failure for security-sensitive decisions like rate-limit keys and IP allowlists.
 */
export declare function clientIp(request: Request): string;
/**
 * Compute a before/after diff of top-level scalar (string/number/boolean) keys
 * between a stored record and an incoming patch. Used to audit settings changes
 * without ever logging nested objects (which may hold secret blobs).
 */
export declare function diffScalarKeys(previous: Record<string, unknown>, next: Record<string, unknown>): Array<{
    key: string;
    from: unknown;
    to: unknown;
}>;
export declare const MAX_CONCURRENT_SESSIONS = 5;
/**
 * After a successful primary-credential check, prune the user's active sessions
 * down to the configured concurrent maximum (default 5; configurable via
 * `auth.maxConcurrentSessions`). Strategy is "revoke oldest" so a user can
 * always sign in.
 */
export declare function enforceSessionLimitsForUser(d: any, userId: string): Promise<void>;
export declare function getAdminPath(): string;
/**
 * Resolve a collection definition from the runtime config, tolerating both
 * the array and the record (keyed-by-slug) shapes consumers use.
 */
export declare function findCollectionDef(slug: string): CollectionDefinition | undefined;
/**
 * True when `collection` declares a `sections` JSON field — the storage
 * contract the flat Page Editor + section endpoints rely on. Guards against
 * silently dropping section writes on a collection that never declared the
 * field (the field-access layer strips undeclared keys).
 */
export declare function collectionSupportsSections(slug: string): boolean;
/**
 * Gate a per-document read for endpoints (e.g. SEO analysis) that fetch a
 * document directly rather than through the collection CRUD routes. Enforces
 * both the API-key collection scope and the collection's `access.read` rule
 * so an AUTHOR scoped to one collection cannot read another's content.
 * Returns a `Response` to short-circuit with, or `null` when allowed.
 */
export declare function enforceDocumentReadAccess(session: AuthSession, collection: string): Promise<Response | null>;
//# sourceMappingURL=route-helpers.d.ts.map