/**
 * Asset upload and serving API routes.
 *
 * POST /api/repos/:repo/entities/:type/:id/assets — multipart image upload
 * GET  /api/assets/*                               — serve local assets
 */
import type { FastifyInstance, FastifyReply } from 'fastify';
import type { WorkspaceManager } from '../../core/workspace-manager.js';
import { AuthService } from '../../services/auth-service.js';
/** Whether the same-origin proxy transport serves this file type (images +
 *  fonts — the subresources a CSP-pinned sandboxed iframe loads). The share
 *  asset route uses this to pick proxy vs the default serve for other types
 *  (e.g. a PDF download link on a shared doc keeps the cached R2 302). */
export declare function isProxyableAssetFilename(filename: string): boolean;
/**
 * Serve an immutable-key asset's bytes once authorization has already been
 * decided by the caller. This is the shared serving entry behind BOTH the
 * authed `/api/assets/:assetId/:filename` route (authz = hasRepoAccess) and the
 * public `/api/share/:token/asset/...` route (authz = valid token + repo scope).
 *
 * It does serving ONLY — R2 302-redirect vs local stream, path-segment
 * hardening, and cache headers. The index lookup, filename match, and the
 * access decision stay in each route so neither surface can drift into serving
 * a file the other wouldn't authorize.
 *
 * Mechanically this is a THIN WRAPPER over the generic `serveBackendObject`
 * primitive (the same primitive that backs conversation-scratch images), so
 * both surfaces share one serving codepath. Two asset-specific concerns stay
 * here as pre-steps rather than leaking into the generic primitive:
 *   1. The R2 FONT branch — fonts are proxied through the app origin (not 302'd)
 *      with cross-origin CORS headers, and HEAD is answered from the indexed
 *      size WITHOUT downloading the bytes. This is unique to the catalog.
 *   2. The catalog's stricter local segment hardening (rejects a `.`-prefixed
 *      filename, `/` or `\` in either segment), preserved verbatim.
 *
 * The owning `repo` must be resolved by the caller (from the asset index) so the
 * correct graph/backend is selected. Always fail-closed with 404 — never leak
 * existence.
 */
export declare function serveIndexedAsset(workspaceManager: WorkspaceManager, reply: FastifyReply, opts: {
    assetId: string;
    filename: string;
    repo: string;
    isHead: boolean;
    size?: number | null;
    /** Artifact-asset token path: serve same-origin (no R2 302) with
     *  revalidate+ETag so the route's per-serve access re-check stays
     *  authoritative and the CSP-locked sandbox can load the bytes. */
    proxyR2?: boolean;
    /** Caller's If-None-Match, so the proxy path can answer 304. */
    ifNoneMatch?: string;
}): Promise<unknown>;
/**
 * Generic backend-object serving primitive (composer-vision-attach / E2a). Serves
 * bytes for a RAW backend key once the caller has decided authorization: R2 → 302
 * to a method-bound signed URL; local → stream from `.studiograph/assets/{key}`
 * with a sandbox CSP + nosniff. The R2 signer is INJECTED so the caller controls
 * which keyspace is signed (catalog asset vs conversation-scratch image), and the
 * cache header may be a single value or a per-backend pair. Always fail-closed 404.
 *
 * This is the single serving codepath behind BOTH `serveIndexedAsset` (the
 * catalog, which layers an R2 font pre-step + stricter segment hardening on top)
 * and the conversation-scratch image route.
 */
export declare function serveBackendObject(workspaceManager: WorkspaceManager, reply: FastifyReply, opts: {
    /** Any repo whose graph reaches the workspace-global backend. */
    repo: string;
    /** Full backend key, e.g. `chat-scratch/{sessionId}/{imageId}/{filename}`. */
    key: string;
    /** Display filename — drives content-type fallback + headers. */
    filename: string;
    /** Explicit content type (from the metadata row). Falls back to ext. */
    contentType?: string;
    isHead: boolean;
    /**
     * Cache-Control. A single string is used for both backends; a `{r2, local}`
     * pair lets callers diverge (catalog: R2 private/4h vs local immutable/1y).
     */
    cacheControl: string | {
        r2: string;
        local: string;
    };
    /** On the local path, emit cross-origin font CORS headers (catalog fonts). */
    fontHeaders?: boolean;
    /** 404 body message (catalog uses 'Asset not found'). Defaults to 'Not found'. */
    notFoundError?: string;
    /** Method-bound R2 signer for `key` (only invoked on the R2 backend). */
    signR2Url: (method: 'GET' | 'HEAD') => Promise<string>;
}): Promise<unknown>;
export declare function registerAssetApiRoutes(fastify: FastifyInstance, workspaceManager: WorkspaceManager, authService: AuthService): Promise<void>;
