/** Task status, not asset status. `success` is the only state that carries a URL. */ export type GenerationStatus = "pending" | "processing" | "success" | "failed"; export type StudioStyle = "none" | "photo" | "flatlay" | "studio" | "warm" | "illus"; /** * The four ratios the studio presents. * * The server accepts ten; offering four is a product choice, the same asymmetry * RATIO_CHOICES already has in the picker. */ export type StudioRatio = "1x1" | "4x3" | "3x4" | "16x9"; export declare const STUDIO_STYLES: readonly StudioStyle[]; export declare const STUDIO_RATIOS: readonly StudioRatio[]; export declare const STUDIO_COUNTS: readonly number[]; export interface ComposeRequest { prompt: string; style?: StudioStyle; ratio: StudioRatio; count: number; /** Mutually exclusive with refTmpToken. */ refAssetId?: string; /** Only with refAssetId; selects a stored variant instead of the original. */ refRatio?: string; /** Mutually exclusive with refAssetId. Comes from createRefUploadIntent. */ refTmpToken?: string; /** 10–90. Anything outside that is replaced server-side by the default, 55. */ refStrength?: number; restaurantId?: string; } /** * One image of a queued batch. * * There is deliberately no `url` field. Unlike a ratio variant — whose URL serves the * original while generation is outstanding — a composed image has no original to fall back * on, so a URL before `success` would be a real 404. Render from `status`. */ export interface ComposeItem { taskId: string; assetId: string; imageId: string; status: GenerationStatus; /** Present when this one image could not be queued; the rest of the batch still was. */ error?: string; } export interface ComposeResponse { batchId: string; prompt: string; style: StudioStyle; /** Canonicalised server-side, so it may differ from what was sent. */ ratio: string; items: ComposeItem[]; } /** Progress for one image. The last four fields appear only once status is `success`. */ export interface BatchItem { taskId: string; assetId: string; imageId: string; status: GenerationStatus; /** Distinguishes "retrying" from "about to give up"; the cap is 3. */ attempts: number; error?: string; url?: string; width?: number; height?: number; fileSizeBytes?: number; } export interface BatchView { batchId: string; prompt: string; ratio: string; items: BatchItem[]; } export interface RefUploadIntent { /** Send this back as ComposeRequest.refTmpToken. */ token: string; objectKey: string; uploadUrl: string; method: "PUT"; expiresAt: string; } /** * Queues one generation task per requested image and returns immediately. * * Resolves with every item `pending`. Partial success is a real outcome rather than an error: * an item that could not be queued comes back `failed` with an `error` while the others are * still `pending`, so inspect items rather than treating the call as all-or-nothing. */ export declare function composeImages(businessId: string, req: ComposeRequest): Promise; /** Polls one batch. 404 for an unknown batch, and for another business's batch. */ export declare function fetchBatch(businessId: string, batchId: string): Promise; /** * Signs a PUT for a reference image. * * The bytes go straight to R2 and no asset row is created — a reference is an input, not a * library item, and the merchant did not ask to store it. */ export declare function createRefUploadIntent(businessId: string, req: { contentType: string; fileSize: number; }): Promise; /** * PUTs the reference bytes to the signed URL. * * Deliberately NOT through mediaClient: this request goes to R2 rather than to * media-backend, and sending our Authorization header to a presigned URL makes S3 reject it. * Content-Type must be exactly what the intent was requested with, because the signature * binds it. */ export declare function uploadRefImage(intent: RefUploadIntent, file: File): Promise;