@gallop.software/studio 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/handlers/index.ts","../../src/handlers/list.ts","../../src/handlers/utils/meta.ts","../../src/handlers/utils/files.ts","../../src/handlers/utils/thumbnails.ts","../../src/handlers/utils/cdn.ts","../../src/handlers/files.ts","../../src/handlers/images.ts","../../src/handlers/scan.ts","../../src/handlers/import.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server'\n\n// List handlers\nimport { handleList, handleSearch, handleListFolders, handleCountImages, handleFolderImages } from './list'\n\n// File handlers\nimport { handleUpload, handleDelete, handleCreateFolder, handleRename, handleMove, handleMoveStream } from './files'\n\n// Image handlers\nimport { handleSync, handleReprocess, handleProcessAllStream } from './images'\n\n// Scan handler\nimport { handleScanStream, handleDeleteOrphans } from './scan'\n\n// Import handlers\nimport { handleImportUrls, handleGetCdns, handleUpdateCdns } from './import'\n\n/**\n * Unified GET handler for all Studio API routes\n */\nexport async function GET(request: NextRequest) {\n if (process.env.NODE_ENV !== 'development') {\n return NextResponse.json({ error: 'Not available in production' }, { status: 403 })\n }\n\n const pathname = request.nextUrl.pathname\n const route = pathname.replace(/^\\/api\\/studio\\/?/, '')\n\n // Route: /api/studio/list-folders (must come before 'list' check)\n if (route === 'list-folders') {\n return handleListFolders()\n }\n\n // Route: /api/studio/list\n if (route === 'list' || route.startsWith('list')) {\n return handleList(request)\n }\n\n // Route: /api/studio/count-images\n if (route === 'count-images') {\n return handleCountImages()\n }\n\n // Route: /api/studio/folder-images\n if (route === 'folder-images') {\n return handleFolderImages(request)\n }\n\n // Route: /api/studio/search\n if (route === 'search') {\n return handleSearch(request)\n }\n\n // Route: /api/studio/cdns\n if (route === 'cdns') {\n return handleGetCdns()\n }\n\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n}\n\n/**\n * Unified POST handler for all Studio API routes\n */\nexport async function POST(request: NextRequest) {\n if (process.env.NODE_ENV !== 'development') {\n return NextResponse.json({ error: 'Not available in production' }, { status: 403 })\n }\n\n const pathname = request.nextUrl.pathname\n const route = pathname.replace(/^\\/api\\/studio\\/?/, '')\n\n // Route: /api/studio/upload\n if (route === 'upload') {\n return handleUpload(request)\n }\n\n // Route: /api/studio/delete\n if (route === 'delete') {\n return handleDelete(request)\n }\n\n // Route: /api/studio/sync\n if (route === 'sync') {\n return handleSync(request)\n }\n\n // Route: /api/studio/reprocess\n if (route === 'reprocess') {\n return handleReprocess(request)\n }\n\n // Route: /api/studio/process-all (streaming)\n if (route === 'process-all') {\n return handleProcessAllStream()\n }\n\n // Route: /api/studio/create-folder\n if (route === 'create-folder') {\n return handleCreateFolder(request)\n }\n\n // Route: /api/studio/rename\n if (route === 'rename') {\n return handleRename(request)\n }\n\n // Route: /api/studio/move (streaming)\n if (route === 'move') {\n return handleMoveStream(request)\n }\n\n // Route: /api/studio/scan (streaming)\n if (route === 'scan') {\n return handleScanStream()\n }\n\n // Route: /api/studio/delete-orphans\n if (route === 'delete-orphans') {\n return handleDeleteOrphans(request)\n }\n\n // Route: /api/studio/import (streaming)\n if (route === 'import') {\n return handleImportUrls(request)\n }\n\n // Route: /api/studio/cdns (update)\n if (route === 'cdns') {\n return handleUpdateCdns(request)\n }\n\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n}\n\n/**\n * Unified DELETE handler\n */\nexport async function DELETE(request: NextRequest) {\n if (process.env.NODE_ENV !== 'development') {\n return NextResponse.json({ error: 'Not available in production' }, { status: 403 })\n }\n\n return handleDelete(request)\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport type { FileItem, MetaEntry } from '../types'\nimport { loadMeta, isImageFile, getCdnUrls, getFileEntries } from './utils'\nimport { getThumbnailPath, isProcessed } from '../types'\n\n/**\n * List files and folders from meta\n * Folders are derived from file paths in meta AND filesystem\n */\nexport async function handleList(request: NextRequest) {\n const searchParams = request.nextUrl.searchParams\n const requestedPath = searchParams.get('path') || 'public'\n\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n \n // Normalize the requested path to match meta keys\n // requestedPath is like \"public\" or \"public/photos\"\n // meta keys are like \"/photos/image.jpg\"\n const relativePath = requestedPath.replace(/^public\\/?/, '')\n const pathPrefix = relativePath ? `/${relativePath}/` : '/'\n\n const items: FileItem[] = []\n const seenFolders = new Set<string>()\n const metaKeys = fileEntries.map(([key]) => key)\n \n // Check if we're inside the images folder (protected area)\n const isInsideImagesFolder = relativePath === 'images' || relativePath.startsWith('images/')\n \n // Also check filesystem for folders (including empty ones)\n const absoluteDir = path.join(process.cwd(), requestedPath)\n try {\n const dirEntries = await fs.readdir(absoluteDir, { withFileTypes: true })\n for (const entry of dirEntries) {\n if (entry.name.startsWith('.')) continue\n \n if (entry.isDirectory()) {\n if (!seenFolders.has(entry.name)) {\n seenFolders.add(entry.name)\n \n // Check if this folder is the images folder or inside it\n const isImagesFolder = entry.name === 'images' && !relativePath\n const folderPath = relativePath ? `public/${relativePath}/${entry.name}` : `public/${entry.name}`\n \n // Count files in this folder - from filesystem if inside images, from meta otherwise\n let fileCount = 0\n if (isInsideImagesFolder || isImagesFolder) {\n // Count files from filesystem for images folder\n const subDir = path.join(absoluteDir, entry.name)\n try {\n const subEntries = await fs.readdir(subDir)\n fileCount = subEntries.filter(f => !f.startsWith('.')).length\n } catch { /* ignore */ }\n } else {\n // Count files from meta for regular folders\n const folderPrefix = pathPrefix === '/' ? `/${entry.name}/` : `${pathPrefix}${entry.name}/`\n for (const k of metaKeys) {\n if (k.startsWith(folderPrefix)) fileCount++\n }\n }\n \n items.push({\n name: entry.name,\n path: folderPath,\n type: 'folder',\n fileCount,\n isProtected: isImagesFolder || isInsideImagesFolder,\n })\n }\n } else if (isInsideImagesFolder) {\n // List files inside images folder from filesystem (not from meta)\n const filePath = relativePath ? `public/${relativePath}/${entry.name}` : `public/${entry.name}`\n const fullPath = path.join(absoluteDir, entry.name)\n \n let fileSize: number | undefined\n try {\n const stats = await fs.stat(fullPath)\n fileSize = stats.size\n } catch { /* ignore */ }\n \n const isImage = isImageFile(entry.name)\n \n items.push({\n name: entry.name,\n path: filePath,\n type: 'file',\n size: fileSize,\n thumbnail: isImage ? `/${relativePath}/${entry.name}` : undefined,\n hasThumbnail: false,\n isProtected: true,\n })\n }\n }\n } catch {\n // Directory might not exist (all files in cloud)\n }\n \n // If meta is empty and no folders found, return empty with a flag\n if (fileEntries.length === 0 && items.length === 0) {\n return NextResponse.json({ items: [], isEmpty: true })\n }\n\n for (const [key, entry] of fileEntries) {\n // Check if this file is under the current path\n if (!key.startsWith(pathPrefix) && pathPrefix !== '/') continue\n if (pathPrefix === '/' && !key.startsWith('/')) continue\n\n // Get the part after the current path\n const remaining = pathPrefix === '/' ? key.slice(1) : key.slice(pathPrefix.length)\n \n // Skip if empty (shouldn't happen)\n if (!remaining) continue\n\n // Check if there's a subfolder\n const slashIndex = remaining.indexOf('/')\n \n if (slashIndex !== -1) {\n // This is in a subfolder - show the folder\n const folderName = remaining.slice(0, slashIndex)\n \n if (!seenFolders.has(folderName)) {\n seenFolders.add(folderName)\n \n // Count files in this folder from meta\n const folderPrefix = pathPrefix === '/' ? `/${folderName}/` : `${pathPrefix}${folderName}/`\n let fileCount = 0\n for (const k of metaKeys) {\n if (k.startsWith(folderPrefix)) fileCount++\n }\n \n items.push({\n name: folderName,\n path: relativePath ? `public/${relativePath}/${folderName}` : `public/${folderName}`,\n type: 'folder',\n fileCount,\n isProtected: isInsideImagesFolder,\n })\n }\n } else {\n // This is a file in the current folder\n const fileName = remaining\n const isImage = isImageFile(fileName)\n const isPushedToCloud = entry.c !== undefined\n \n // Determine if this is a remote import vs pushed to our R2\n const fileCdnUrl = isPushedToCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n \n let thumbnail: string | undefined\n let hasThumbnail = false\n let fileSize: number | undefined\n \n const entryIsProcessed = isProcessed(entry)\n \n if (isImage && entryIsProcessed) {\n // Has been processed - use thumbnail\n const thumbPath = getThumbnailPath(key, 'sm')\n \n if (isPushedToCloud && entry.c !== undefined) {\n // CDN thumbnail - get URL from _cdns array\n const cdnUrl = cdnUrls[entry.c]\n if (cdnUrl) {\n thumbnail = `${cdnUrl}${thumbPath}`\n hasThumbnail = true\n }\n } else {\n // Local thumbnail - check if exists\n const localThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n await fs.access(localThumbPath)\n thumbnail = thumbPath\n hasThumbnail = true\n } catch {\n // Thumbnail doesn't exist yet\n thumbnail = key\n hasThumbnail = false\n }\n }\n } else if (isImage) {\n // Not processed yet - use original (from CDN if available)\n if (isPushedToCloud && entry.c !== undefined) {\n const cdnUrl = cdnUrls[entry.c]\n thumbnail = cdnUrl ? `${cdnUrl}${key}` : key\n } else {\n thumbnail = key\n }\n hasThumbnail = false\n }\n \n // Try to get file size if file exists locally\n if (!isPushedToCloud) {\n try {\n const filePath = path.join(process.cwd(), 'public', key)\n const stats = await fs.stat(filePath)\n fileSize = stats.size\n } catch {\n // File might not exist locally (synced)\n }\n }\n \n items.push({\n name: fileName,\n path: relativePath ? `public/${relativePath}/${fileName}` : `public/${fileName}`,\n type: 'file',\n size: fileSize,\n thumbnail,\n hasThumbnail,\n isProcessed: entryIsProcessed,\n cdnPushed: isPushedToCloud,\n cdnBaseUrl: fileCdnUrl,\n isRemote,\n isProtected: isInsideImagesFolder,\n dimensions: entry.o ? { width: entry.o.w, height: entry.o.h } : undefined,\n })\n }\n }\n\n return NextResponse.json({ items })\n } catch (error) {\n console.error('Failed to list directory:', error)\n return NextResponse.json({ error: 'Failed to list directory' }, { status: 500 })\n }\n}\n\nexport async function handleSearch(request: NextRequest) {\n const searchParams = request.nextUrl.searchParams\n const query = searchParams.get('q')?.toLowerCase() || ''\n \n if (query.length < 2) {\n return NextResponse.json({ items: [] })\n }\n\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n const items: FileItem[] = []\n\n for (const [key, entry] of fileEntries) {\n // Check if the path matches the query\n if (!key.toLowerCase().includes(query)) continue\n \n const fileName = path.basename(key)\n const relativePath = key.slice(1) // Remove leading /\n const isImage = isImageFile(fileName)\n const isPushedToCloud = entry.c !== undefined\n \n // Determine if this is a remote import vs pushed to our R2\n const fileCdnUrl = isPushedToCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n \n let thumbnail: string | undefined\n let hasThumbnail = false\n const entryIsProcessed = isProcessed(entry)\n \n if (isImage && entryIsProcessed) {\n // Has been processed - use thumbnail\n const thumbPath = getThumbnailPath(key, 'sm')\n \n if (isPushedToCloud && entry.c !== undefined) {\n const cdnUrl = cdnUrls[entry.c]\n if (cdnUrl) {\n thumbnail = `${cdnUrl}${thumbPath}`\n hasThumbnail = true\n }\n } else {\n const localThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n await fs.access(localThumbPath)\n thumbnail = thumbPath\n hasThumbnail = true\n } catch {\n thumbnail = key\n hasThumbnail = false\n }\n }\n } else if (isImage) {\n // Not processed yet - use original (from CDN if available)\n if (isPushedToCloud && entry.c !== undefined) {\n const cdnUrl = cdnUrls[entry.c]\n thumbnail = cdnUrl ? `${cdnUrl}${key}` : key\n } else {\n thumbnail = key\n }\n hasThumbnail = false\n }\n \n items.push({\n name: fileName,\n path: `public/${relativePath}`,\n type: 'file',\n thumbnail,\n hasThumbnail,\n isProcessed: entryIsProcessed,\n cdnPushed: isPushedToCloud,\n cdnBaseUrl: fileCdnUrl,\n isRemote,\n dimensions: entry.o ? { width: entry.o.w, height: entry.o.h } : undefined,\n })\n }\n\n return NextResponse.json({ items })\n } catch (error) {\n console.error('Failed to search:', error)\n return NextResponse.json({ error: 'Failed to search' }, { status: 500 })\n }\n}\n\nexport async function handleListFolders() {\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const folderSet = new Set<string>()\n \n // Extract all folder paths from meta keys\n for (const [key] of fileEntries) {\n const parts = key.split('/')\n // Build up folder paths: /photos/2024/image.jpg -> photos, photos/2024\n let current = ''\n for (let i = 1; i < parts.length - 1; i++) {\n current = current ? `${current}/${parts[i]}` : parts[i]\n folderSet.add(current)\n }\n }\n \n // Also scan filesystem recursively for folders (including empty ones)\n async function scanDir(dir: string, relativePath: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'images') {\n const folderRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n folderSet.add(folderRelPath)\n // Recurse into subdirectory\n await scanDir(path.join(dir, entry.name), folderRelPath)\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n \n const publicDir = path.join(process.cwd(), 'public')\n await scanDir(publicDir, '')\n \n const folders: { path: string; name: string; depth: number }[] = []\n folders.push({ path: 'public', name: 'public', depth: 0 })\n \n const sortedFolders = Array.from(folderSet).sort()\n for (const folderPath of sortedFolders) {\n const depth = folderPath.split('/').length\n const name = folderPath.split('/').pop() || folderPath\n folders.push({\n path: `public/${folderPath}`,\n name,\n depth\n })\n }\n\n return NextResponse.json({ folders })\n } catch (error) {\n console.error('Failed to list folders:', error)\n return NextResponse.json({ error: 'Failed to list folders' }, { status: 500 })\n }\n}\n\nexport async function handleCountImages() {\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const allImages: string[] = []\n\n for (const [key] of fileEntries) {\n const fileName = path.basename(key)\n if (isImageFile(fileName)) {\n allImages.push(key.slice(1)) // Remove leading /\n }\n }\n\n return NextResponse.json({\n count: allImages.length,\n images: allImages,\n })\n } catch (error) {\n console.error('Failed to count images:', error)\n return NextResponse.json({ error: 'Failed to count images' }, { status: 500 })\n }\n}\n\nexport async function handleFolderImages(request: NextRequest) {\n try {\n const searchParams = request.nextUrl.searchParams\n const foldersParam = searchParams.get('folders')\n \n if (!foldersParam) {\n return NextResponse.json({ error: 'No folders provided' }, { status: 400 })\n }\n\n const folders = foldersParam.split(',')\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const allImages: string[] = []\n\n // Convert folder paths to prefixes for matching\n const prefixes = folders.map(f => {\n const rel = f.replace(/^public\\/?/, '')\n return rel ? `/${rel}/` : '/'\n })\n\n for (const [key] of fileEntries) {\n const fileName = path.basename(key)\n if (!isImageFile(fileName)) continue\n \n // Check if this image is in one of the requested folders\n for (const prefix of prefixes) {\n if (key.startsWith(prefix) || (prefix === '/' && key.startsWith('/'))) {\n allImages.push(key.slice(1)) // Remove leading /\n break\n }\n }\n }\n\n return NextResponse.json({\n count: allImages.length,\n images: allImages,\n })\n } catch (error) {\n console.error('Failed to get folder images:', error)\n return NextResponse.json({ error: 'Failed to get folder images' }, { status: 500 })\n }\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\nimport type { FullMeta, MetaEntry } from '../../types'\n\nexport async function loadMeta(): Promise<FullMeta> {\n const metaPath = path.join(process.cwd(), '_data', '_studio.json')\n \n try {\n const content = await fs.readFile(metaPath, 'utf-8')\n return JSON.parse(content) as FullMeta\n } catch {\n return {}\n }\n}\n\nexport async function saveMeta(meta: FullMeta): Promise<void> {\n const dataDir = path.join(process.cwd(), '_data')\n await fs.mkdir(dataDir, { recursive: true })\n const metaPath = path.join(dataDir, '_studio.json')\n \n // Ensure _cdns is at the top by creating ordered object\n const ordered: FullMeta = {}\n if (meta._cdns) {\n ordered._cdns = meta._cdns\n }\n // Add all other entries\n for (const [key, value] of Object.entries(meta)) {\n if (key !== '_cdns') {\n ordered[key] = value\n }\n }\n \n await fs.writeFile(metaPath, JSON.stringify(ordered, null, 2))\n}\n\n/**\n * Get the CDN URLs array from meta\n */\nexport function getCdnUrls(meta: FullMeta): string[] {\n return meta._cdns || []\n}\n\n/**\n * Set the CDN URLs array in meta\n */\nexport function setCdnUrls(meta: FullMeta, urls: string[]): void {\n meta._cdns = urls\n}\n\n/**\n * Get or add a CDN URL, returning its index\n */\nexport function getOrAddCdnIndex(meta: FullMeta, cdnUrl: string): number {\n if (!meta._cdns) {\n meta._cdns = []\n }\n \n // Normalize URL (remove trailing slash)\n const normalizedUrl = cdnUrl.replace(/\\/$/, '')\n \n const existingIndex = meta._cdns.indexOf(normalizedUrl)\n if (existingIndex >= 0) {\n return existingIndex\n }\n \n // Add new CDN URL\n meta._cdns.push(normalizedUrl)\n return meta._cdns.length - 1\n}\n\n/**\n * Get a meta entry (excludes special keys like _cdns)\n */\nexport function getMetaEntry(meta: FullMeta, key: string): MetaEntry | undefined {\n if (key.startsWith('_')) return undefined\n const value = meta[key]\n if (Array.isArray(value)) return undefined\n return value as MetaEntry | undefined\n}\n\n/**\n * Set a meta entry\n */\nexport function setMetaEntry(meta: FullMeta, key: string, entry: MetaEntry): void {\n meta[key] = entry\n}\n\n/**\n * Delete a meta entry\n */\nexport function deleteMetaEntry(meta: FullMeta, key: string): void {\n delete meta[key]\n}\n\n/**\n * Get all file entries (excludes special keys like _cdns)\n */\nexport function getFileEntries(meta: FullMeta): Array<[string, MetaEntry]> {\n return Object.entries(meta).filter(\n ([key, value]) => !key.startsWith('_') && !Array.isArray(value)\n ) as Array<[string, MetaEntry]>\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\n\nexport function isImageFile(filename: string): boolean {\n const ext = path.extname(filename).toLowerCase()\n return ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.tiff', '.tif'].includes(ext)\n}\n\nexport function isMediaFile(filename: string): boolean {\n const ext = path.extname(filename).toLowerCase()\n // Images\n if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.tiff', '.tif'].includes(ext)) return true\n // Videos\n if (['.mp4', '.webm', '.mov', '.avi', '.mkv', '.m4v'].includes(ext)) return true\n // Audio\n if (['.mp3', '.wav', '.ogg', '.m4a', '.flac', '.aac'].includes(ext)) return true\n // Documents/PDFs\n if (['.pdf'].includes(ext)) return true\n return false\n}\n\nexport function getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase()\n switch (ext) {\n case '.jpg':\n case '.jpeg':\n return 'image/jpeg'\n case '.png':\n return 'image/png'\n case '.gif':\n return 'image/gif'\n case '.webp':\n return 'image/webp'\n case '.svg':\n return 'image/svg+xml'\n default:\n return 'application/octet-stream'\n }\n}\n\nexport async function getFolderStats(folderPath: string): Promise<{ fileCount: number; totalSize: number }> {\n let fileCount = 0\n let totalSize = 0\n\n async function scanFolder(dir: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n await scanFolder(fullPath)\n } else if (isMediaFile(entry.name)) {\n fileCount++\n const stats = await fs.stat(fullPath)\n totalSize += stats.size\n }\n }\n } catch { /* ignore errors */ }\n }\n\n await scanFolder(folderPath)\n return { fileCount, totalSize }\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\nimport sharp from 'sharp'\nimport { encode } from 'blurhash'\nimport type { MetaEntry, Dimensions } from '../../types'\n\nexport const FULL_MAX_WIDTH = 2560\n\nexport const DEFAULT_SIZES: Record<string, { width: number; suffix: string; key: 'sm' | 'md' | 'lg' }> = {\n small: { width: 300, suffix: '-sm', key: 'sm' },\n medium: { width: 700, suffix: '-md', key: 'md' },\n large: { width: 1400, suffix: '-lg', key: 'lg' },\n}\n\nexport async function processImage(\n buffer: Buffer,\n imageKey: string\n): Promise<MetaEntry> {\n const sharpInstance = sharp(buffer)\n const metadata = await sharpInstance.metadata()\n const originalWidth = metadata.width || 0\n const originalHeight = metadata.height || 0\n const ratio = originalHeight / originalWidth\n\n // Remove leading slash for path operations\n const keyWithoutSlash = imageKey.startsWith('/') ? imageKey.slice(1) : imageKey\n const baseName = path.basename(keyWithoutSlash, path.extname(keyWithoutSlash))\n const ext = path.extname(keyWithoutSlash).toLowerCase()\n const imageDir = path.dirname(keyWithoutSlash)\n\n const imagesPath = path.join(process.cwd(), 'public', 'images', imageDir === '.' ? '' : imageDir)\n await fs.mkdir(imagesPath, { recursive: true })\n\n const isPng = ext === '.png'\n const outputExt = isPng ? '.png' : '.jpg'\n\n // Build the result entry\n const entry: MetaEntry = {\n o: { w: originalWidth, h: originalHeight },\n }\n\n // Generate full size (capped at FULL_MAX_WIDTH)\n const fullFileName = imageDir === '.' ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`\n const fullPath = path.join(process.cwd(), 'public', 'images', fullFileName)\n\n let fullWidth = originalWidth\n let fullHeight = originalHeight\n\n if (originalWidth > FULL_MAX_WIDTH) {\n fullWidth = FULL_MAX_WIDTH\n fullHeight = Math.round(FULL_MAX_WIDTH * ratio)\n if (isPng) {\n await sharp(buffer).resize(fullWidth, fullHeight).png({ quality: 85 }).toFile(fullPath)\n } else {\n await sharp(buffer).resize(fullWidth, fullHeight).jpeg({ quality: 85 }).toFile(fullPath)\n }\n } else {\n if (isPng) {\n await sharp(buffer).png({ quality: 85 }).toFile(fullPath)\n } else {\n await sharp(buffer).jpeg({ quality: 85 }).toFile(fullPath)\n }\n }\n entry.f = { w: fullWidth, h: fullHeight }\n\n // Generate thumbnail sizes\n for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {\n const { width: maxWidth, suffix, key } = sizeConfig\n if (originalWidth <= maxWidth) {\n continue // Skip if original is smaller than this size\n }\n\n const newHeight = Math.round(maxWidth * ratio)\n const sizeFileName = `${baseName}${suffix}${outputExt}`\n const sizeFilePath = imageDir === '.' ? sizeFileName : `${imageDir}/${sizeFileName}`\n const sizePath = path.join(process.cwd(), 'public', 'images', sizeFilePath)\n\n if (isPng) {\n await sharp(buffer).resize(maxWidth, newHeight).png({ quality: 80 }).toFile(sizePath)\n } else {\n await sharp(buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath)\n }\n\n entry[key] = { w: maxWidth, h: newHeight }\n }\n\n // Generate blurhash\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n\n entry.b = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4)\n\n return entry\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\nimport { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'\nimport { getAllThumbnailPaths } from '../../types'\nimport { getContentType } from './files'\n\n/**\n * Purge URLs from Cloudflare cache\n * Requires CLOUDFLARE_ZONE_ID and CLOUDFLARE_API_TOKEN environment variables\n */\nexport async function purgeCloudflareCache(urls: string[]): Promise<void> {\n const zoneId = process.env.CLOUDFLARE_ZONE_ID\n const apiToken = process.env.CLOUDFLARE_API_TOKEN\n \n if (!zoneId || !apiToken || urls.length === 0) {\n return // Cache purge not configured or no URLs to purge\n }\n \n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${apiToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ files: urls }),\n }\n )\n \n if (!response.ok) {\n console.error('Cache purge failed:', await response.text())\n }\n } catch (error) {\n console.error('Cache purge error:', error)\n }\n}\n\nfunction getR2Client() {\n const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID\n const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID\n const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY\n\n if (!accountId || !accessKeyId || !secretAccessKey) {\n throw new Error('R2 not configured')\n }\n\n return new S3Client({\n region: 'auto',\n endpoint: `https://${accountId}.r2.cloudflarestorage.com`,\n credentials: { accessKeyId, secretAccessKey },\n })\n}\n\nexport async function downloadFromCdn(originalPath: string): Promise<Buffer> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n\n const response = await r2.send(\n new GetObjectCommand({\n Bucket: bucketName,\n Key: originalPath.replace(/^\\//, ''),\n })\n )\n\n const stream = response.Body as NodeJS.ReadableStream\n const chunks: Buffer[] = []\n for await (const chunk of stream) {\n chunks.push(Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\nexport async function uploadToCdn(imageKey: string): Promise<void> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n\n // Upload all thumbnail sizes derived from imageKey\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n const fileBuffer = await fs.readFile(localPath)\n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: thumbPath.replace(/^\\//, ''),\n Body: fileBuffer,\n ContentType: getContentType(thumbPath),\n })\n )\n } catch {\n // File might not exist (e.g., if image is smaller than thumbnail size)\n }\n }\n}\n\nexport async function deleteLocalThumbnails(imageKey: string): Promise<void> {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n await fs.unlink(localPath)\n } catch {\n // File might not exist\n }\n }\n}\n\n/**\n * Download image from a remote URL (not R2)\n */\nexport async function downloadFromRemoteUrl(url: string): Promise<Buffer> {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(`Failed to download from ${url}: ${response.status}`)\n }\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n}\n\n/**\n * Upload original image to R2 CDN\n */\nexport async function uploadOriginalToCdn(imageKey: string): Promise<void> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n const localPath = path.join(process.cwd(), 'public', imageKey)\n const fileBuffer = await fs.readFile(localPath)\n \n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: imageKey.replace(/^\\//, ''),\n Body: fileBuffer,\n ContentType: getContentType(imageKey),\n })\n )\n}\n\n/**\n * Delete original and thumbnails from R2 CDN\n */\nexport async function deleteFromCdn(imageKey: string, hasThumbnails: boolean): Promise<void> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n\n // Delete original\n try {\n await r2.send(\n new DeleteObjectCommand({\n Bucket: bucketName,\n Key: imageKey.replace(/^\\//, ''),\n })\n )\n } catch {\n // May not exist\n }\n\n // Delete thumbnails if they exist\n if (hasThumbnails) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n try {\n await r2.send(\n new DeleteObjectCommand({\n Bucket: bucketName,\n Key: thumbPath.replace(/^\\//, ''),\n })\n )\n } catch {\n // May not exist\n }\n }\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport sharp from 'sharp'\nimport type { MetaEntry } from '../types'\nimport { getAllThumbnailPaths, isProcessed } from '../types'\nimport { \n loadMeta, \n saveMeta, \n isImageFile, \n isMediaFile,\n getCdnUrls,\n downloadFromCdn,\n downloadFromRemoteUrl,\n uploadOriginalToCdn,\n uploadToCdn,\n deleteFromCdn,\n deleteLocalThumbnails,\n processImage,\n} from './utils'\n\nexport async function handleUpload(request: NextRequest) {\n try {\n const formData = await request.formData()\n const file = formData.get('file') as File | null\n const targetPath = formData.get('path') as string || 'public'\n\n if (!file) {\n return NextResponse.json({ error: 'No file provided' }, { status: 400 })\n }\n\n const bytes = await file.arrayBuffer()\n const buffer = Buffer.from(bytes)\n\n const fileName = file.name\n const ext = path.extname(fileName).toLowerCase()\n\n const isImage = isImageFile(fileName)\n const isMedia = isMediaFile(fileName)\n\n const meta = await loadMeta()\n\n let relativeDir = ''\n if (targetPath === 'public') {\n relativeDir = ''\n } else if (targetPath.startsWith('public/')) {\n relativeDir = targetPath.replace('public/', '')\n }\n \n if (relativeDir === 'images' || relativeDir.startsWith('images/')) {\n return NextResponse.json(\n { error: 'Cannot upload to images/ folder. Upload to public/ instead - thumbnails are generated automatically.' },\n { status: 400 }\n )\n }\n\n // Build the meta key\n let imageKey = '/' + (relativeDir ? `${relativeDir}/${fileName}` : fileName)\n\n // Check for collision - rename if needed\n if (meta[imageKey]) {\n const baseName = path.basename(fileName, ext)\n let counter = 1\n let newFileName = `${baseName}-${counter}${ext}`\n let newKey = '/' + (relativeDir ? `${relativeDir}/${newFileName}` : newFileName)\n \n while (meta[newKey]) {\n counter++\n newFileName = `${baseName}-${counter}${ext}`\n newKey = '/' + (relativeDir ? `${relativeDir}/${newFileName}` : newFileName)\n }\n \n imageKey = newKey\n }\n\n // Extract actual filename from key\n const actualFileName = path.basename(imageKey)\n \n const uploadDir = path.join(process.cwd(), 'public', relativeDir)\n await fs.mkdir(uploadDir, { recursive: true })\n await fs.writeFile(path.join(uploadDir, actualFileName), buffer)\n\n if (!isMedia) {\n return NextResponse.json({ \n success: true, \n message: 'File uploaded (not a media file)',\n path: `public/${relativeDir ? relativeDir + '/' : ''}${actualFileName}`\n })\n }\n\n // Add to meta\n if (isImage && ext !== '.svg') {\n // Read dimensions for images\n try {\n const metadata = await sharp(buffer).metadata()\n meta[imageKey] = {\n o: { w: metadata.width || 0, h: metadata.height || 0 },\n }\n } catch {\n meta[imageKey] = { o: { w: 0, h: 0 } }\n }\n } else {\n // Non-image media or SVG\n meta[imageKey] = {}\n }\n\n await saveMeta(meta)\n\n return NextResponse.json({ \n success: true, \n imageKey,\n message: 'File uploaded. Run \"Process Images\" to generate thumbnails.'\n })\n } catch (error) {\n console.error('Failed to upload:', error)\n const message = error instanceof Error ? error.message : 'Unknown error'\n return NextResponse.json({ error: `Failed to upload file: ${message}` }, { status: 500 })\n }\n}\n\nexport async function handleDelete(request: NextRequest) {\n try {\n const { paths } = await request.json() as { paths: string[] }\n\n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n return NextResponse.json({ error: 'No paths provided' }, { status: 400 })\n }\n\n const meta = await loadMeta()\n const deleted: string[] = []\n const errors: string[] = []\n\n for (const itemPath of paths) {\n try {\n if (!itemPath.startsWith('public/')) {\n errors.push(`Invalid path: ${itemPath}`)\n continue\n }\n\n const absolutePath = path.join(process.cwd(), itemPath)\n const imageKey = '/' + itemPath.replace(/^public\\//, '')\n \n // Check if this is in meta (could be synced with no local file)\n const entry = meta[imageKey] as MetaEntry | undefined\n const isPushedToCloud = entry?.c !== undefined\n \n // Try to delete local file/folder\n try {\n const stats = await fs.stat(absolutePath)\n\n if (stats.isDirectory()) {\n await fs.rm(absolutePath, { recursive: true })\n \n // Remove all meta entries under this folder\n const prefix = imageKey + '/'\n for (const key of Object.keys(meta)) {\n if (key.startsWith(prefix) || key === imageKey) {\n const keyEntry = meta[key] as MetaEntry | undefined\n // Also delete local thumbnails if not synced\n if (keyEntry && keyEntry.c === undefined) {\n for (const thumbPath of getAllThumbnailPaths(key)) {\n const absoluteThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try { await fs.unlink(absoluteThumbPath) } catch { /* ignore */ }\n }\n }\n delete meta[key]\n }\n }\n } else {\n await fs.unlink(absolutePath)\n\n const isInImagesFolder = itemPath.startsWith('public/images/')\n \n if (!isInImagesFolder && entry) {\n // Delete local thumbnails if not synced\n if (!isPushedToCloud) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const absoluteThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try { await fs.unlink(absoluteThumbPath) } catch { /* ignore */ }\n }\n }\n delete meta[imageKey]\n }\n }\n } catch {\n // File doesn't exist locally - might be synced\n if (entry) {\n // Just remove from meta (file is on CDN)\n delete meta[imageKey]\n } else {\n // Check if it's a folder prefix in meta\n const prefix = imageKey + '/'\n let foundAny = false\n for (const key of Object.keys(meta)) {\n if (key.startsWith(prefix)) {\n delete meta[key]\n foundAny = true\n }\n }\n if (!foundAny) {\n errors.push(`Not found: ${itemPath}`)\n continue\n }\n }\n }\n\n deleted.push(itemPath)\n } catch (error) {\n console.error(`Failed to delete ${itemPath}:`, error)\n errors.push(itemPath)\n }\n }\n\n await saveMeta(meta)\n\n return NextResponse.json({\n success: true,\n deleted,\n errors: errors.length > 0 ? errors : undefined,\n })\n } catch (error) {\n console.error('Failed to delete:', error)\n return NextResponse.json({ error: 'Failed to delete files' }, { status: 500 })\n }\n}\n\nexport async function handleCreateFolder(request: NextRequest) {\n try {\n const { parentPath, name } = await request.json()\n\n if (!name || typeof name !== 'string') {\n return NextResponse.json({ error: 'Folder name is required' }, { status: 400 })\n }\n\n const sanitizedName = name.replace(/[<>:\"/\\\\|?*]/g, '').trim()\n if (!sanitizedName) {\n return NextResponse.json({ error: 'Invalid folder name' }, { status: 400 })\n }\n\n const safePath = (parentPath || 'public').replace(/\\.\\./g, '')\n const folderPath = path.join(process.cwd(), safePath, sanitizedName)\n\n if (!folderPath.startsWith(path.join(process.cwd(), 'public'))) {\n return NextResponse.json({ error: 'Invalid path' }, { status: 400 })\n }\n\n try {\n await fs.access(folderPath)\n return NextResponse.json({ error: 'A folder with this name already exists' }, { status: 400 })\n } catch {\n // Good - folder doesn't exist\n }\n\n await fs.mkdir(folderPath, { recursive: true })\n\n return NextResponse.json({ success: true, path: path.join(safePath, sanitizedName) })\n } catch (error) {\n console.error('Failed to create folder:', error)\n return NextResponse.json({ error: 'Failed to create folder' }, { status: 500 })\n }\n}\n\nexport async function handleRename(request: NextRequest) {\n try {\n const { oldPath, newName } = await request.json()\n\n if (!oldPath || !newName) {\n return NextResponse.json({ error: 'Path and new name are required' }, { status: 400 })\n }\n\n const sanitizedName = newName.replace(/[<>:\"/\\\\|?*]/g, '').trim()\n if (!sanitizedName) {\n return NextResponse.json({ error: 'Invalid name' }, { status: 400 })\n }\n\n const safePath = oldPath.replace(/\\.\\./g, '')\n const absoluteOldPath = path.join(process.cwd(), safePath)\n const parentDir = path.dirname(absoluteOldPath)\n const absoluteNewPath = path.join(parentDir, sanitizedName)\n\n if (!absoluteOldPath.startsWith(path.join(process.cwd(), 'public'))) {\n return NextResponse.json({ error: 'Invalid path' }, { status: 400 })\n }\n\n try {\n await fs.access(absoluteOldPath)\n } catch {\n return NextResponse.json({ error: 'File or folder not found' }, { status: 404 })\n }\n\n try {\n await fs.access(absoluteNewPath)\n return NextResponse.json({ error: 'An item with this name already exists' }, { status: 400 })\n } catch {\n // Good - new path doesn't exist\n }\n\n const stats = await fs.stat(absoluteOldPath)\n const isFile = stats.isFile()\n const isImage = isFile && isImageFile(path.basename(oldPath))\n\n await fs.rename(absoluteOldPath, absoluteNewPath)\n\n if (isImage) {\n const meta = await loadMeta()\n const oldRelativePath = safePath.replace(/^public\\//, '')\n const newRelativePath = path.join(path.dirname(oldRelativePath), sanitizedName)\n const oldKey = '/' + oldRelativePath\n const newKey = '/' + newRelativePath\n\n if (meta[oldKey]) {\n const entry = meta[oldKey]\n\n const oldThumbPaths = getAllThumbnailPaths(oldKey)\n const newThumbPaths = getAllThumbnailPaths(newKey)\n\n for (let i = 0; i < oldThumbPaths.length; i++) {\n const oldThumbPath = path.join(process.cwd(), 'public', oldThumbPaths[i])\n const newThumbPath = path.join(process.cwd(), 'public', newThumbPaths[i])\n \n await fs.mkdir(path.dirname(newThumbPath), { recursive: true })\n \n try {\n await fs.rename(oldThumbPath, newThumbPath)\n } catch {\n // Thumbnail might not exist\n }\n }\n\n delete meta[oldKey]\n meta[newKey] = entry\n }\n\n await saveMeta(meta)\n }\n\n const newPath = path.join(path.dirname(safePath), sanitizedName)\n return NextResponse.json({ success: true, newPath })\n } catch (error) {\n console.error('Failed to rename:', error)\n return NextResponse.json({ error: 'Failed to rename' }, { status: 500 })\n }\n}\n\nexport async function handleMoveStream(request: NextRequest) {\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const { paths, destination } = await request.json()\n\n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n sendEvent({ type: 'error', message: 'Paths are required' })\n controller.close()\n return\n }\n\n if (!destination || typeof destination !== 'string') {\n sendEvent({ type: 'error', message: 'Destination is required' })\n controller.close()\n return\n }\n\n const safeDestination = destination.replace(/\\.\\./g, '')\n const absoluteDestination = path.join(process.cwd(), safeDestination)\n\n if (!absoluteDestination.startsWith(path.join(process.cwd(), 'public'))) {\n sendEvent({ type: 'error', message: 'Invalid destination' })\n controller.close()\n return\n }\n\n // Ensure destination folder exists\n await fs.mkdir(absoluteDestination, { recursive: true })\n\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n\n const moved: string[] = []\n const errors: string[] = []\n const total = paths.length\n\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < paths.length; i++) {\n const itemPath = paths[i]\n const safePath = itemPath.replace(/\\.\\./g, '')\n const itemName = path.basename(safePath)\n const newAbsolutePath = path.join(absoluteDestination, itemName)\n\n // Build meta keys\n const oldRelativePath = safePath.replace(/^public\\//, '')\n const newRelativePath = path.join(safeDestination.replace(/^public\\//, ''), itemName)\n const oldKey = '/' + oldRelativePath\n const newKey = '/' + newRelativePath\n\n sendEvent({\n type: 'progress',\n current: i + 1,\n total,\n percent: Math.round(((i + 1) / total) * 100),\n currentFile: itemName,\n })\n\n // Check if destination already exists in meta\n if (meta[newKey]) {\n errors.push(`${itemName} already exists in destination`)\n continue\n }\n\n const entry = meta[oldKey] as MetaEntry | undefined\n const isImage = isImageFile(itemName)\n\n // Determine if cloud or remote\n const isInCloud = entry?.c !== undefined\n const fileCdnUrl = isInCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl\n const hasProcessedThumbnails = isProcessed(entry)\n\n try {\n if (isRemote && isImage) {\n // ===== REMOTE IMAGE =====\n const remoteUrl = `${fileCdnUrl}${oldKey}`\n const buffer = await downloadFromRemoteUrl(remoteUrl)\n \n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n const newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n }\n delete meta[oldKey]\n meta[newKey] = newEntry\n moved.push(itemPath)\n\n } else if (isPushedToR2 && isImage) {\n // ===== CLOUD IMAGE (R2) =====\n const buffer = await downloadFromCdn(oldKey)\n \n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n let newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n }\n \n if (hasProcessedThumbnails) {\n const processedEntry = await processImage(buffer, newKey)\n newEntry = { ...newEntry, ...processedEntry }\n }\n \n await uploadOriginalToCdn(newKey)\n \n if (hasProcessedThumbnails) {\n await uploadToCdn(newKey)\n }\n \n await deleteFromCdn(oldKey, hasProcessedThumbnails)\n \n try { await fs.unlink(newAbsolutePath) } catch { /* ignore */ }\n if (hasProcessedThumbnails) {\n await deleteLocalThumbnails(newKey)\n }\n \n newEntry.c = entry?.c\n \n delete meta[oldKey]\n meta[newKey] = newEntry\n moved.push(itemPath)\n\n } else {\n // ===== LOCAL FILE =====\n const absolutePath = path.join(process.cwd(), safePath)\n\n if (absoluteDestination.startsWith(absolutePath + path.sep)) {\n errors.push(`Cannot move ${itemName} into itself`)\n continue\n }\n\n try {\n await fs.access(absolutePath)\n } catch {\n errors.push(`${itemName} not found`)\n continue\n }\n\n try {\n await fs.access(newAbsolutePath)\n errors.push(`${itemName} already exists in destination`)\n continue\n } catch {\n // Good\n }\n\n await fs.rename(absolutePath, newAbsolutePath)\n\n const stats = await fs.stat(newAbsolutePath)\n if (stats.isFile() && isImage && entry) {\n const oldThumbPaths = getAllThumbnailPaths(oldKey)\n const newThumbPaths = getAllThumbnailPaths(newKey)\n\n for (let j = 0; j < oldThumbPaths.length; j++) {\n const oldThumbPath = path.join(process.cwd(), 'public', oldThumbPaths[j])\n const newThumbPath = path.join(process.cwd(), 'public', newThumbPaths[j])\n \n await fs.mkdir(path.dirname(newThumbPath), { recursive: true })\n\n try {\n await fs.rename(oldThumbPath, newThumbPath)\n } catch {\n // Thumbnail might not exist\n }\n }\n\n delete meta[oldKey]\n meta[newKey] = entry\n } else if (stats.isDirectory()) {\n const oldPrefix = oldKey + '/'\n const newPrefix = newKey + '/'\n \n for (const key of Object.keys(meta)) {\n if (key.startsWith(oldPrefix)) {\n const newMetaKey = newPrefix + key.slice(oldPrefix.length)\n meta[newMetaKey] = meta[key]\n delete meta[key]\n }\n }\n }\n\n moved.push(itemPath)\n }\n } catch (err) {\n console.error(`Failed to move ${itemName}:`, err)\n errors.push(`Failed to move ${itemName}`)\n }\n }\n\n await saveMeta(meta)\n\n sendEvent({\n type: 'complete',\n moved: moved.length,\n errors: errors.length,\n errorMessages: errors,\n })\n } catch (error) {\n console.error('Failed to move:', error)\n sendEvent({ type: 'error', message: 'Failed to move items' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n\nexport async function handleMove(request: NextRequest) {\n try {\n const { paths, destination } = await request.json()\n\n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n return NextResponse.json({ error: 'Paths are required' }, { status: 400 })\n }\n\n if (!destination || typeof destination !== 'string') {\n return NextResponse.json({ error: 'Destination is required' }, { status: 400 })\n }\n\n const safeDestination = destination.replace(/\\.\\./g, '')\n const absoluteDestination = path.join(process.cwd(), safeDestination)\n\n if (!absoluteDestination.startsWith(path.join(process.cwd(), 'public'))) {\n return NextResponse.json({ error: 'Invalid destination' }, { status: 400 })\n }\n\n // Ensure destination folder exists (create if needed)\n await fs.mkdir(absoluteDestination, { recursive: true })\n\n const moved: string[] = []\n const errors: string[] = []\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n let metaChanged = false\n\n for (const itemPath of paths) {\n const safePath = itemPath.replace(/\\.\\./g, '')\n const itemName = path.basename(safePath)\n const newAbsolutePath = path.join(absoluteDestination, itemName)\n\n // Build meta keys\n const oldRelativePath = safePath.replace(/^public\\//, '')\n const newRelativePath = path.join(safeDestination.replace(/^public\\//, ''), itemName)\n const oldKey = '/' + oldRelativePath\n const newKey = '/' + newRelativePath\n\n // Check if destination already exists in meta\n if (meta[newKey]) {\n errors.push(`${itemName} already exists in destination`)\n continue\n }\n\n const entry = meta[oldKey] as MetaEntry | undefined\n const isImage = isImageFile(itemName)\n\n // Determine if cloud or remote\n const isInCloud = entry?.c !== undefined\n const fileCdnUrl = isInCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl\n const hasProcessedThumbnails = isProcessed(entry)\n\n try {\n if (isRemote && isImage) {\n // ===== REMOTE IMAGE: Download from external URL, save locally, remove c =====\n const remoteUrl = `${fileCdnUrl}${oldKey}`\n const buffer = await downloadFromRemoteUrl(remoteUrl)\n \n // Save to new local location\n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n // Update meta: remove c (now local), keep other properties\n const newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n // Don't copy thumbnail dims since remote images don't have local thumbnails\n // Don't copy c since it's now local\n }\n delete meta[oldKey]\n meta[newKey] = newEntry\n metaChanged = true\n moved.push(itemPath)\n\n } else if (isPushedToR2 && isImage) {\n // ===== CLOUD IMAGE (R2): Download, move, re-upload, delete old =====\n \n // Download original from R2\n const buffer = await downloadFromCdn(oldKey)\n \n // Save to new local location\n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n // Create new meta entry\n let newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n }\n \n // If processed, regenerate thumbnails\n if (hasProcessedThumbnails) {\n const processedEntry = await processImage(buffer, newKey)\n newEntry = { ...newEntry, ...processedEntry }\n }\n \n // Upload original to new R2 location\n await uploadOriginalToCdn(newKey)\n \n // If processed, upload thumbnails to R2\n if (hasProcessedThumbnails) {\n await uploadToCdn(newKey)\n }\n \n // Delete old files from R2\n await deleteFromCdn(oldKey, hasProcessedThumbnails)\n \n // Delete local files (keep cloud-only state)\n try { await fs.unlink(newAbsolutePath) } catch { /* ignore */ }\n if (hasProcessedThumbnails) {\n await deleteLocalThumbnails(newKey)\n }\n \n // Set c to same CDN index\n newEntry.c = entry?.c\n \n // Update meta\n delete meta[oldKey]\n meta[newKey] = newEntry\n metaChanged = true\n moved.push(itemPath)\n\n } else {\n // ===== LOCAL FILE: Use standard fs.rename =====\n const absolutePath = path.join(process.cwd(), safePath)\n\n if (absoluteDestination.startsWith(absolutePath + path.sep)) {\n errors.push(`Cannot move ${itemName} into itself`)\n continue\n }\n\n try {\n await fs.access(absolutePath)\n } catch {\n errors.push(`${itemName} not found`)\n continue\n }\n\n try {\n await fs.access(newAbsolutePath)\n errors.push(`${itemName} already exists in destination`)\n continue\n } catch {\n // Good - doesn't exist\n }\n\n await fs.rename(absolutePath, newAbsolutePath)\n\n const stats = await fs.stat(newAbsolutePath)\n if (stats.isFile() && isImage && entry) {\n // Move local thumbnails\n const oldThumbPaths = getAllThumbnailPaths(oldKey)\n const newThumbPaths = getAllThumbnailPaths(newKey)\n\n for (let i = 0; i < oldThumbPaths.length; i++) {\n const oldThumbPath = path.join(process.cwd(), 'public', oldThumbPaths[i])\n const newThumbPath = path.join(process.cwd(), 'public', newThumbPaths[i])\n \n await fs.mkdir(path.dirname(newThumbPath), { recursive: true })\n\n try {\n await fs.rename(oldThumbPath, newThumbPath)\n } catch {\n // Thumbnail might not exist\n }\n }\n\n delete meta[oldKey]\n meta[newKey] = entry\n metaChanged = true\n } else if (stats.isDirectory()) {\n // Move folder: update all meta entries under this folder\n const oldPrefix = oldKey + '/'\n const newPrefix = newKey + '/'\n \n for (const key of Object.keys(meta)) {\n if (key.startsWith(oldPrefix)) {\n const newMetaKey = newPrefix + key.slice(oldPrefix.length)\n meta[newMetaKey] = meta[key]\n delete meta[key]\n metaChanged = true\n }\n }\n }\n\n moved.push(itemPath)\n }\n } catch (err) {\n console.error(`Failed to move ${itemName}:`, err)\n errors.push(`Failed to move ${itemName}`)\n }\n }\n\n if (metaChanged) {\n await saveMeta(meta)\n }\n\n return NextResponse.json({\n success: errors.length === 0,\n moved,\n errors: errors.length > 0 ? errors : undefined\n })\n } catch (error) {\n console.error('Failed to move:', error)\n return NextResponse.json({ error: 'Failed to move items' }, { status: 500 })\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'\nimport { getAllThumbnailPaths, isProcessed } from '../types'\nimport {\n loadMeta,\n saveMeta,\n isImageFile,\n getContentType,\n processImage,\n downloadFromCdn,\n uploadToCdn,\n deleteLocalThumbnails,\n getOrAddCdnIndex,\n getFileEntries,\n getMetaEntry,\n getCdnUrls,\n downloadFromRemoteUrl,\n purgeCloudflareCache,\n} from './utils'\n\nexport async function handleSync(request: NextRequest) {\n const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID\n const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID\n const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/\\s*$/, '')\n\n if (!accountId || !accessKeyId || !secretAccessKey || !bucketName || !publicUrl) {\n return NextResponse.json(\n { error: 'R2 not configured. Set CLOUDFLARE_R2_* environment variables.' },\n { status: 400 }\n )\n }\n\n try {\n const { imageKeys } = await request.json() as { imageKeys: string[] }\n\n if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {\n return NextResponse.json({ error: 'No image keys provided' }, { status: 400 })\n }\n\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n \n // Get or add CDN URL to the _cdns array\n const cdnIndex = getOrAddCdnIndex(meta, publicUrl)\n\n const r2 = new S3Client({\n region: 'auto',\n endpoint: `https://${accountId}.r2.cloudflarestorage.com`,\n credentials: { accessKeyId, secretAccessKey },\n })\n\n const pushed: string[] = []\n const errors: string[] = []\n const urlsToPurge: string[] = []\n\n for (let imageKey of imageKeys) {\n // Normalize key to have leading /\n if (!imageKey.startsWith('/')) {\n imageKey = `/${imageKey}`\n }\n \n const entry = getMetaEntry(meta, imageKey)\n if (!entry) {\n errors.push(`Image not found in meta: ${imageKey}. Run Scan first.`)\n continue\n }\n\n // Check if already pushed to our R2\n const existingCdnUrl = entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isAlreadyInOurR2 = existingCdnUrl === publicUrl\n \n if (isAlreadyInOurR2) {\n pushed.push(imageKey)\n continue\n }\n\n // Check if this is a remote image (in another CDN)\n const isRemote = entry.c !== undefined && existingCdnUrl !== publicUrl\n\n try {\n let originalBuffer: Buffer\n\n if (isRemote) {\n // Download from remote URL\n const remoteUrl = `${existingCdnUrl}${imageKey}`\n originalBuffer = await downloadFromRemoteUrl(remoteUrl)\n } else {\n // Read from local file\n const originalLocalPath = path.join(process.cwd(), 'public', imageKey)\n try {\n originalBuffer = await fs.readFile(originalLocalPath)\n } catch {\n errors.push(`Original file not found: ${imageKey}`)\n continue\n }\n }\n\n // Upload original to R2\n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: imageKey.replace(/^\\//, ''),\n Body: originalBuffer,\n ContentType: getContentType(imageKey),\n })\n )\n urlsToPurge.push(`${publicUrl}${imageKey}`)\n\n // Upload thumbnails (only if processed locally, not for remote imports)\n if (!isRemote && isProcessed(entry)) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n const fileBuffer = await fs.readFile(localPath)\n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: thumbPath.replace(/^\\//, ''),\n Body: fileBuffer,\n ContentType: getContentType(thumbPath),\n })\n )\n urlsToPurge.push(`${publicUrl}${thumbPath}`)\n } catch {\n // Thumbnail might not exist\n }\n }\n }\n\n entry.c = cdnIndex\n\n // Delete local files (only for non-remote, local images being pushed)\n if (!isRemote) {\n const originalLocalPath = path.join(process.cwd(), 'public', imageKey)\n \n // Delete local thumbnails\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try { await fs.unlink(localPath) } catch { /* ignore */ }\n }\n\n // Delete local original\n try { await fs.unlink(originalLocalPath) } catch { /* ignore */ }\n }\n\n pushed.push(imageKey)\n } catch (error) {\n console.error(`Failed to push ${imageKey}:`, error)\n errors.push(`Failed to push: ${imageKey}`)\n }\n }\n\n await saveMeta(meta)\n \n // Purge Cloudflare cache for uploaded files\n if (urlsToPurge.length > 0) {\n await purgeCloudflareCache(urlsToPurge)\n }\n\n return NextResponse.json({\n success: true,\n pushed,\n errors: errors.length > 0 ? errors : undefined,\n })\n } catch (error) {\n console.error('Failed to push:', error)\n return NextResponse.json({ error: 'Failed to push to CDN' }, { status: 500 })\n }\n}\n\nexport async function handleReprocess(request: NextRequest) {\n const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/\\s*$/, '')\n \n try {\n const { imageKeys } = await request.json() as { imageKeys: string[] }\n\n if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {\n return NextResponse.json({ error: 'No image keys provided' }, { status: 400 })\n }\n\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const processed: string[] = []\n const errors: string[] = []\n const urlsToPurge: string[] = []\n\n for (let imageKey of imageKeys) {\n // Normalize key to have leading /\n if (!imageKey.startsWith('/')) {\n imageKey = `/${imageKey}`\n }\n \n try {\n let buffer: Buffer\n const entry = getMetaEntry(meta, imageKey)\n const existingCdnIndex = entry?.c\n const existingCdnUrl = existingCdnIndex !== undefined ? cdnUrls[existingCdnIndex] : undefined\n \n // Determine if this is our R2 or a remote CDN\n const isInOurR2 = existingCdnUrl === publicUrl\n const isRemote = existingCdnIndex !== undefined && !isInOurR2\n \n const originalPath = path.join(process.cwd(), 'public', imageKey)\n \n try {\n buffer = await fs.readFile(originalPath)\n } catch {\n if (isInOurR2) {\n // Download original from our R2\n buffer = await downloadFromCdn(imageKey)\n const dir = path.dirname(originalPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(originalPath, buffer)\n } else if (isRemote && existingCdnUrl) {\n // Download from remote URL\n const remoteUrl = `${existingCdnUrl}${imageKey}`\n buffer = await downloadFromRemoteUrl(remoteUrl)\n const dir = path.dirname(originalPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(originalPath, buffer)\n } else {\n throw new Error(`File not found: ${imageKey}`)\n }\n }\n\n const updatedEntry = await processImage(buffer, imageKey)\n // No need to set p flag - presence of thumbnail dims (sm/md/lg/f) indicates processed\n \n if (isInOurR2) {\n // Re-upload thumbnails to R2 and clean up local files\n updatedEntry.c = existingCdnIndex\n await uploadToCdn(imageKey)\n \n // Collect URLs to purge\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n urlsToPurge.push(`${publicUrl}${thumbPath}`)\n }\n \n await deleteLocalThumbnails(imageKey)\n // Delete local original\n try { await fs.unlink(originalPath) } catch { /* ignore */ }\n } else if (isRemote) {\n // Remote image processed locally - remove c flag, now it's local\n // Keep the original and thumbnails locally\n }\n \n meta[imageKey] = updatedEntry\n processed.push(imageKey)\n } catch (error) {\n console.error(`Failed to reprocess ${imageKey}:`, error)\n errors.push(imageKey)\n }\n }\n\n await saveMeta(meta)\n \n // Purge Cloudflare cache for re-uploaded thumbnails\n if (urlsToPurge.length > 0) {\n await purgeCloudflareCache(urlsToPurge)\n }\n\n return NextResponse.json({\n success: true,\n processed,\n errors: errors.length > 0 ? errors : undefined,\n })\n } catch (error) {\n console.error('Failed to reprocess:', error)\n return NextResponse.json({ error: 'Failed to reprocess images' }, { status: 500 })\n }\n}\n\nexport async function handleProcessAllStream() {\n const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/\\s*$/, '')\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const processed: string[] = []\n const errors: string[] = []\n const orphansRemoved: string[] = []\n const urlsToPurge: string[] = []\n\n // Count images in different states\n let alreadyProcessed = 0\n\n // Get all images from meta that need processing (no p flag = not processed yet)\n const imagesToProcess: Array<{ key: string; entry: import('../types').MetaEntry }> = []\n \n for (const [key, entry] of getFileEntries(meta)) {\n const fileName = path.basename(key)\n if (!isImageFile(fileName)) continue\n \n // Check if needs processing (no thumbnail dims = not processed yet)\n if (!isProcessed(entry)) {\n imagesToProcess.push({ key, entry })\n } else {\n alreadyProcessed++\n }\n }\n\n const total = imagesToProcess.length\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < imagesToProcess.length; i++) {\n const { key, entry } = imagesToProcess[i]\n const fullPath = path.join(process.cwd(), 'public', key)\n const existingCdnIndex = entry.c\n const existingCdnUrl = existingCdnIndex !== undefined ? cdnUrls[existingCdnIndex] : undefined\n \n // Determine if this is our R2 or a remote CDN\n const isInOurR2 = existingCdnUrl === publicUrl\n const isRemote = existingCdnIndex !== undefined && !isInOurR2\n \n sendEvent({ \n type: 'progress', \n current: i + 1, \n total, \n percent: Math.round(((i + 1) / total) * 100),\n currentFile: key.slice(1) // Remove leading /\n })\n\n try {\n let buffer: Buffer\n \n // Download from appropriate source\n if (isInOurR2) {\n buffer = await downloadFromCdn(key)\n const dir = path.dirname(fullPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(fullPath, buffer)\n } else if (isRemote && existingCdnUrl) {\n const remoteUrl = `${existingCdnUrl}${key}`\n buffer = await downloadFromRemoteUrl(remoteUrl)\n const dir = path.dirname(fullPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(fullPath, buffer)\n } else {\n buffer = await fs.readFile(fullPath)\n }\n \n const ext = path.extname(key).toLowerCase()\n const isSvg = ext === '.svg'\n\n if (isSvg) {\n const imageDir = path.dirname(key.slice(1))\n const imagesPath = path.join(process.cwd(), 'public', 'images', imageDir === '.' ? '' : imageDir)\n await fs.mkdir(imagesPath, { recursive: true })\n \n const fileName = path.basename(key)\n const destPath = path.join(imagesPath, fileName)\n await fs.writeFile(destPath, buffer)\n\n meta[key] = {\n ...entry,\n o: { w: 0, h: 0 },\n b: '',\n f: { w: 0, h: 0 }, // SVG has \"full\" to indicate processed\n }\n \n // Remote images become local after processing\n if (isRemote) {\n delete (meta[key] as import('../types').MetaEntry).c\n }\n } else {\n const processedEntry = await processImage(buffer, key)\n meta[key] = {\n ...processedEntry,\n ...(isInOurR2 ? { c: existingCdnIndex } : {}),\n }\n // Remote images become local after processing (no c)\n }\n\n // If image was in our R2, upload new thumbnails and clean up local files\n if (isInOurR2) {\n await uploadToCdn(key)\n \n // Collect URLs to purge\n for (const thumbPath of getAllThumbnailPaths(key)) {\n urlsToPurge.push(`${publicUrl}${thumbPath}`)\n }\n \n await deleteLocalThumbnails(key)\n // Delete local original\n try { await fs.unlink(fullPath) } catch { /* ignore */ }\n }\n // Remote images stay local after processing (original + thumbnails)\n\n processed.push(key.slice(1))\n } catch (error) {\n console.error(`Failed to process ${key}:`, error)\n errors.push(key.slice(1))\n }\n }\n\n sendEvent({ type: 'cleanup', message: 'Removing orphaned thumbnails...' })\n \n // Build set of expected thumbnail paths\n const trackedPaths = new Set<string>()\n for (const [imageKey, entry] of getFileEntries(meta)) {\n // Only track local thumbnails (not pushed to CDN)\n if (entry.c === undefined) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n trackedPaths.add(thumbPath)\n }\n }\n }\n\n async function findOrphans(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n \n for (const fsEntry of entries) {\n if (fsEntry.name.startsWith('.')) continue\n\n const entryFullPath = path.join(dir, fsEntry.name)\n const relPath = relativePath ? `${relativePath}/${fsEntry.name}` : fsEntry.name\n\n if (fsEntry.isDirectory()) {\n await findOrphans(entryFullPath, relPath)\n } else if (isImageFile(fsEntry.name)) {\n const publicPath = `/images/${relPath}`\n if (!trackedPaths.has(publicPath)) {\n try {\n await fs.unlink(entryFullPath)\n orphansRemoved.push(publicPath)\n } catch (err) {\n console.error(`Failed to remove orphan ${publicPath}:`, err)\n }\n }\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n const imagesDir = path.join(process.cwd(), 'public', 'images')\n try {\n await findOrphans(imagesDir)\n } catch {\n // images dir might not exist\n }\n\n async function removeEmptyDirs(dir: string): Promise<boolean> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n let isEmpty = true\n\n for (const fsEntry of entries) {\n if (fsEntry.isDirectory()) {\n const subDirEmpty = await removeEmptyDirs(path.join(dir, fsEntry.name))\n if (!subDirEmpty) isEmpty = false\n } else {\n isEmpty = false\n }\n }\n\n if (isEmpty && dir !== imagesDir) {\n await fs.rmdir(dir)\n }\n\n return isEmpty\n } catch {\n return true\n }\n }\n\n try {\n await removeEmptyDirs(imagesDir)\n } catch {\n // images dir might not exist\n }\n \n await saveMeta(meta)\n \n // Purge Cloudflare cache for re-uploaded thumbnails\n if (urlsToPurge.length > 0) {\n await purgeCloudflareCache(urlsToPurge)\n }\n\n sendEvent({ \n type: 'complete', \n processed: processed.length, \n alreadyProcessed,\n orphansRemoved: orphansRemoved.length,\n errors: errors.length,\n })\n } catch (error) {\n console.error('Failed to process all:', error)\n sendEvent({ type: 'error', message: 'Failed to process images' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport sharp from 'sharp'\nimport { encode } from 'blurhash'\nimport { loadMeta, saveMeta, isMediaFile, isImageFile, getFileEntries } from './utils'\nimport { getAllThumbnailPaths, isProcessed } from '../types'\n\n/**\n * Streaming scan handler - scans filesystem for new files not in meta\n * For images, reads dimensions (w/h)\n * Handles collisions by renaming files with -1, -2, etc.\n * Also detects orphaned files in the images folder\n */\nexport async function handleScanStream() {\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const meta = await loadMeta()\n const existingCount = Object.keys(meta).filter(k => !k.startsWith('_')).length\n const existingKeys = new Set(Object.keys(meta))\n const added: string[] = []\n const renamed: Array<{ from: string; to: string }> = []\n const errors: string[] = []\n const orphanedFiles: string[] = []\n\n // Collect all files first\n const allFiles: Array<{ relativePath: string; fullPath: string }> = []\n\n async function scanDir(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n \n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n \n const fullPath = path.join(dir, entry.name)\n const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n\n // Skip the images folder (generated thumbnails)\n if (relPath === 'images' || relPath.startsWith('images/')) continue\n\n if (entry.isDirectory()) {\n await scanDir(fullPath, relPath)\n } else if (isMediaFile(entry.name)) {\n allFiles.push({ relativePath: relPath, fullPath })\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n const publicDir = path.join(process.cwd(), 'public')\n await scanDir(publicDir)\n\n const total = allFiles.length\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < allFiles.length; i++) {\n let { relativePath, fullPath } = allFiles[i]\n let imageKey = '/' + relativePath\n \n sendEvent({ \n type: 'progress', \n current: i + 1, \n total, \n percent: Math.round(((i + 1) / total) * 100),\n currentFile: relativePath \n })\n\n // Check if already in meta\n if (existingKeys.has(imageKey)) {\n // File already tracked - skip\n continue\n }\n\n // Check for collision (path exists in meta but file is new)\n if (meta[imageKey]) {\n // Need to rename this file to avoid collision\n const ext = path.extname(relativePath)\n const baseName = relativePath.slice(0, -ext.length)\n let counter = 1\n let newKey = `/${baseName}-${counter}${ext}`\n \n while (meta[newKey]) {\n counter++\n newKey = `/${baseName}-${counter}${ext}`\n }\n \n // Rename the physical file\n const newRelativePath = `${baseName}-${counter}${ext}`\n const newFullPath = path.join(process.cwd(), 'public', newRelativePath)\n \n try {\n await fs.rename(fullPath, newFullPath)\n renamed.push({ from: relativePath, to: newRelativePath })\n relativePath = newRelativePath\n fullPath = newFullPath\n imageKey = newKey\n } catch (err) {\n console.error(`Failed to rename ${relativePath}:`, err)\n errors.push(`Failed to rename ${relativePath}`)\n continue\n }\n }\n\n try {\n const isImage = isImageFile(relativePath)\n \n if (isImage) {\n // Read dimensions and generate blurhash for images\n const ext = path.extname(relativePath).toLowerCase()\n \n if (ext === '.svg') {\n // SVGs don't have pixel dimensions in the same way\n meta[imageKey] = { o: { w: 0, h: 0 }, b: '' }\n } else {\n try {\n const buffer = await fs.readFile(fullPath)\n const metadata = await sharp(buffer).metadata()\n \n // Generate blurhash\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n \n const blurhash = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4)\n \n meta[imageKey] = {\n o: { w: metadata.width || 0, h: metadata.height || 0 },\n b: blurhash,\n }\n } catch {\n // Couldn't read dimensions\n meta[imageKey] = { o: { w: 0, h: 0 } }\n }\n }\n } else {\n // Non-image files - just add empty entry\n meta[imageKey] = {}\n }\n \n existingKeys.add(imageKey)\n added.push(imageKey)\n } catch (error) {\n console.error(`Failed to process ${relativePath}:`, error)\n errors.push(relativePath)\n }\n }\n\n // Check for orphaned files in the images folder\n sendEvent({ type: 'cleanup', message: 'Checking for orphaned thumbnails...' })\n \n // Build set of expected thumbnail paths from meta entries\n const expectedThumbnails = new Set<string>()\n const fileEntries = getFileEntries(meta)\n for (const [imageKey, entry] of fileEntries) {\n // Only track local thumbnails (not pushed to CDN)\n if (entry.c === undefined && isProcessed(entry)) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n expectedThumbnails.add(thumbPath)\n }\n }\n }\n\n // Scan the images folder for orphaned files\n async function findOrphans(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n \n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n\n const fullPath = path.join(dir, entry.name)\n const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) {\n await findOrphans(fullPath, relPath)\n } else if (isImageFile(entry.name)) {\n const publicPath = `/images/${relPath}`\n if (!expectedThumbnails.has(publicPath)) {\n orphanedFiles.push(publicPath)\n }\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n const imagesDir = path.join(process.cwd(), 'public', 'images')\n try {\n await findOrphans(imagesDir)\n } catch {\n // images dir might not exist\n }\n\n await saveMeta(meta)\n\n sendEvent({ \n type: 'complete', \n existingCount,\n added: added.length, \n renamed: renamed.length,\n errors: errors.length,\n renamedFiles: renamed,\n orphanedFiles: orphanedFiles.length > 0 ? orphanedFiles : undefined,\n })\n } catch (error) {\n console.error('Scan failed:', error)\n sendEvent({ type: 'error', message: 'Scan failed' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n\n/**\n * Delete orphaned files from the images folder\n */\nexport async function handleDeleteOrphans(request: NextRequest) {\n try {\n const { paths } = await request.json() as { paths: string[] }\n \n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n return NextResponse.json({ error: 'No paths provided' }, { status: 400 })\n }\n \n const deleted: string[] = []\n const errors: string[] = []\n \n for (const orphanPath of paths) {\n // Ensure the path is within the images folder for safety\n if (!orphanPath.startsWith('/images/')) {\n errors.push(`Invalid path: ${orphanPath}`)\n continue\n }\n \n const fullPath = path.join(process.cwd(), 'public', orphanPath)\n \n try {\n await fs.unlink(fullPath)\n deleted.push(orphanPath)\n } catch (err) {\n console.error(`Failed to delete ${orphanPath}:`, err)\n errors.push(orphanPath)\n }\n }\n \n // Clean up empty directories\n const imagesDir = path.join(process.cwd(), 'public', 'images')\n \n async function removeEmptyDirs(dir: string): Promise<boolean> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n let isEmpty = true\n \n for (const entry of entries) {\n if (entry.isDirectory()) {\n const subDirEmpty = await removeEmptyDirs(path.join(dir, entry.name))\n if (!subDirEmpty) isEmpty = false\n } else {\n isEmpty = false\n }\n }\n \n if (isEmpty && dir !== imagesDir) {\n await fs.rmdir(dir)\n }\n \n return isEmpty\n } catch {\n return true\n }\n }\n \n try {\n await removeEmptyDirs(imagesDir)\n } catch {\n // images dir might not exist\n }\n \n return NextResponse.json({\n success: true,\n deleted: deleted.length,\n errors: errors.length,\n })\n } catch (error) {\n console.error('Failed to delete orphans:', error)\n return NextResponse.json({ error: 'Failed to delete orphaned files' }, { status: 500 })\n }\n}\n","import { NextRequest } from 'next/server'\nimport sharp from 'sharp'\nimport { encode } from 'blurhash'\nimport {\n loadMeta,\n saveMeta,\n getOrAddCdnIndex,\n getMetaEntry,\n setMetaEntry,\n} from './utils'\nimport type { Dimensions } from '../types'\n\n/**\n * Parse an image URL into base URL and path\n */\nfunction parseImageUrl(url: string): { base: string; path: string } {\n const parsed = new URL(url)\n // Base is protocol + host\n const base = `${parsed.protocol}//${parsed.host}`\n // Path is everything after\n const path = parsed.pathname\n return { base, path }\n}\n\n/**\n * Fetch remote image and get dimensions + blurhash\n */\nasync function processRemoteImage(url: string): Promise<{ o: Dimensions; b: string }> {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(`Failed to fetch: ${response.status}`)\n }\n \n const buffer = Buffer.from(await response.arrayBuffer())\n \n const metadata = await sharp(buffer).metadata()\n \n // Generate blurhash\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n \n const blurhash = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4)\n \n return {\n o: { w: metadata.width || 0, h: metadata.height || 0 },\n b: blurhash,\n }\n}\n\n/**\n * Streaming endpoint to import images from URLs\n */\nexport async function handleImportUrls(request: NextRequest) {\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const { urls } = await request.json() as { urls: string[] }\n \n if (!urls || !Array.isArray(urls) || urls.length === 0) {\n sendEvent({ type: 'error', message: 'No URLs provided' })\n controller.close()\n return\n }\n\n const meta = await loadMeta()\n const added: string[] = []\n const skipped: string[] = []\n const errors: string[] = []\n\n const total = urls.length\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < urls.length; i++) {\n const url = urls[i].trim()\n if (!url) continue\n \n sendEvent({\n type: 'progress',\n current: i + 1,\n total,\n percent: Math.round(((i + 1) / total) * 100),\n currentFile: url,\n })\n\n try {\n // Parse URL to get base and path\n const { base, path } = parseImageUrl(url)\n \n // Check if this path already exists in meta\n const existingEntry = getMetaEntry(meta, path)\n if (existingEntry) {\n skipped.push(path)\n continue\n }\n \n // Get or add CDN URL to _cdns array\n const cdnIndex = getOrAddCdnIndex(meta, base)\n \n // Fetch and process the image\n const imageData = await processRemoteImage(url)\n \n // Add entry to meta\n // Note: No thumbnail dims since this is an external image, not processed locally\n setMetaEntry(meta, path, {\n o: imageData.o,\n b: imageData.b,\n c: cdnIndex,\n })\n \n added.push(path)\n } catch (error) {\n console.error(`Failed to import ${url}:`, error)\n errors.push(url)\n }\n }\n\n await saveMeta(meta)\n\n sendEvent({\n type: 'complete',\n added: added.length,\n skipped: skipped.length,\n errors: errors.length,\n })\n } catch (error) {\n console.error('Import failed:', error)\n sendEvent({ type: 'error', message: 'Import failed' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n\n/**\n * Get CDN URLs for settings\n */\nexport async function handleGetCdns() {\n try {\n const meta = await loadMeta()\n const cdns = meta._cdns || []\n \n return Response.json({ cdns })\n } catch (error) {\n console.error('Failed to get CDNs:', error)\n return Response.json({ error: 'Failed to get CDNs' }, { status: 500 })\n }\n}\n\n/**\n * Update CDN URLs from settings\n */\nexport async function handleUpdateCdns(request: NextRequest) {\n try {\n const { cdns } = await request.json() as { cdns: string[] }\n \n if (!Array.isArray(cdns)) {\n return Response.json({ error: 'Invalid CDN array' }, { status: 400 })\n }\n \n const meta = await loadMeta()\n \n // Normalize URLs (remove trailing slashes)\n meta._cdns = cdns.map(url => url.replace(/\\/$/, ''))\n \n await saveMeta(meta)\n \n return Response.json({ success: true, cdns: meta._cdns })\n } catch (error) {\n console.error('Failed to update CDNs:', error)\n return Response.json({ error: 'Failed to update CDNs' }, { status: 500 })\n }\n}\n"],"mappings":"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","names":["NextResponse","fs","path","path","fs","path","fs","path","path","fs","path","fs","NextResponse","fs","path","sharp","NextResponse","path","fs","sharp","NextResponse","fs","path","S3Client","PutObjectCommand","NextResponse","S3Client","path","fs","PutObjectCommand","NextResponse","fs","path","sharp","encode","fs","path","sharp","encode","NextResponse","sharp","encode","path","sharp","encode","NextResponse"]}
1
+ {"version":3,"sources":["../../src/handlers/index.ts","../../src/handlers/list.ts","../../src/handlers/utils/meta.ts","../../src/handlers/utils/files.ts","../../src/handlers/utils/thumbnails.ts","../../src/handlers/utils/cdn.ts","../../src/handlers/files.ts","../../src/handlers/images.ts","../../src/handlers/scan.ts","../../src/handlers/import.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server'\n\n// List handlers\nimport { handleList, handleSearch, handleListFolders, handleCountImages, handleFolderImages } from './list'\n\n// File handlers\nimport { handleUpload, handleDelete, handleCreateFolder, handleRename, handleMove, handleMoveStream } from './files'\n\n// Image handlers\nimport { handleSync, handleReprocess, handleProcessAllStream } from './images'\n\n// Scan handler\nimport { handleScanStream, handleDeleteOrphans } from './scan'\n\n// Import handlers\nimport { handleImportUrls, handleGetCdns, handleUpdateCdns } from './import'\n\n/**\n * Unified GET handler for all Studio API routes\n */\nexport async function GET(request: NextRequest) {\n if (process.env.NODE_ENV !== 'development') {\n return NextResponse.json({ error: 'Not available in production' }, { status: 403 })\n }\n\n const pathname = request.nextUrl.pathname\n const route = pathname.replace(/^\\/api\\/studio\\/?/, '')\n\n // Route: /api/studio/list-folders (must come before 'list' check)\n if (route === 'list-folders') {\n return handleListFolders()\n }\n\n // Route: /api/studio/list\n if (route === 'list' || route.startsWith('list')) {\n return handleList(request)\n }\n\n // Route: /api/studio/count-images\n if (route === 'count-images') {\n return handleCountImages()\n }\n\n // Route: /api/studio/folder-images\n if (route === 'folder-images') {\n return handleFolderImages(request)\n }\n\n // Route: /api/studio/search\n if (route === 'search') {\n return handleSearch(request)\n }\n\n // Route: /api/studio/cdns\n if (route === 'cdns') {\n return handleGetCdns()\n }\n\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n}\n\n/**\n * Unified POST handler for all Studio API routes\n */\nexport async function POST(request: NextRequest) {\n if (process.env.NODE_ENV !== 'development') {\n return NextResponse.json({ error: 'Not available in production' }, { status: 403 })\n }\n\n const pathname = request.nextUrl.pathname\n const route = pathname.replace(/^\\/api\\/studio\\/?/, '')\n\n // Route: /api/studio/upload\n if (route === 'upload') {\n return handleUpload(request)\n }\n\n // Route: /api/studio/delete\n if (route === 'delete') {\n return handleDelete(request)\n }\n\n // Route: /api/studio/sync\n if (route === 'sync') {\n return handleSync(request)\n }\n\n // Route: /api/studio/reprocess\n if (route === 'reprocess') {\n return handleReprocess(request)\n }\n\n // Route: /api/studio/process-all (streaming)\n if (route === 'process-all') {\n return handleProcessAllStream()\n }\n\n // Route: /api/studio/create-folder\n if (route === 'create-folder') {\n return handleCreateFolder(request)\n }\n\n // Route: /api/studio/rename\n if (route === 'rename') {\n return handleRename(request)\n }\n\n // Route: /api/studio/move (streaming)\n if (route === 'move') {\n return handleMoveStream(request)\n }\n\n // Route: /api/studio/scan (streaming)\n if (route === 'scan') {\n return handleScanStream()\n }\n\n // Route: /api/studio/delete-orphans\n if (route === 'delete-orphans') {\n return handleDeleteOrphans(request)\n }\n\n // Route: /api/studio/import (streaming)\n if (route === 'import') {\n return handleImportUrls(request)\n }\n\n // Route: /api/studio/cdns (update)\n if (route === 'cdns') {\n return handleUpdateCdns(request)\n }\n\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n}\n\n/**\n * Unified DELETE handler\n */\nexport async function DELETE(request: NextRequest) {\n if (process.env.NODE_ENV !== 'development') {\n return NextResponse.json({ error: 'Not available in production' }, { status: 403 })\n }\n\n return handleDelete(request)\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport type { FileItem, MetaEntry } from '../types'\nimport { loadMeta, isImageFile, getCdnUrls, getFileEntries } from './utils'\nimport { getThumbnailPath, isProcessed } from '../types'\n\n/**\n * Get all thumbnail file info for a processed meta entry\n * Returns the thumbnail paths that exist based on which dimension properties are present\n */\nfunction getExistingThumbnails(originalPath: string, entry: MetaEntry): Array<{ path: string; size: 'f' | 'lg' | 'md' | 'sm' }> {\n const thumbnails: Array<{ path: string; size: 'f' | 'lg' | 'md' | 'sm' }> = []\n \n if (entry.f) {\n thumbnails.push({ path: getThumbnailPath(originalPath, 'full'), size: 'f' })\n }\n if (entry.lg) {\n thumbnails.push({ path: getThumbnailPath(originalPath, 'lg'), size: 'lg' })\n }\n if (entry.md) {\n thumbnails.push({ path: getThumbnailPath(originalPath, 'md'), size: 'md' })\n }\n if (entry.sm) {\n thumbnails.push({ path: getThumbnailPath(originalPath, 'sm'), size: 'sm' })\n }\n \n return thumbnails\n}\n\n/**\n * List files and folders from meta\n * Folders are derived from file paths in meta AND filesystem\n */\nexport async function handleList(request: NextRequest) {\n const searchParams = request.nextUrl.searchParams\n const requestedPath = searchParams.get('path') || 'public'\n\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n \n // Normalize the requested path to match meta keys\n // requestedPath is like \"public\" or \"public/photos\"\n // meta keys are like \"/photos/image.jpg\"\n const relativePath = requestedPath.replace(/^public\\/?/, '')\n const pathPrefix = relativePath ? `/${relativePath}/` : '/'\n\n const items: FileItem[] = []\n const seenFolders = new Set<string>()\n const metaKeys = fileEntries.map(([key]) => key)\n \n // Check if we're inside the images folder (protected area)\n const isInsideImagesFolder = relativePath === 'images' || relativePath.startsWith('images/')\n \n // For the images folder, derive contents from meta entries with thumbnails\n if (isInsideImagesFolder) {\n // Get the path within images folder (e.g., \"images/subfolder\" -> \"subfolder\")\n const imagesSubPath = relativePath.replace(/^images\\/?/, '')\n const imagesPrefix = imagesSubPath ? `/${imagesSubPath}/` : '/'\n \n // Collect all thumbnails from processed entries\n const allThumbnails: Array<{ path: string; size: 'f' | 'lg' | 'md' | 'sm'; originalKey: string }> = []\n \n for (const [key, entry] of fileEntries) {\n if (isProcessed(entry)) {\n const thumbnails = getExistingThumbnails(key, entry)\n for (const thumb of thumbnails) {\n allThumbnails.push({ ...thumb, originalKey: key })\n }\n }\n }\n \n // Filter thumbnails that are in the current images subfolder\n for (const thumb of allThumbnails) {\n // thumb.path is like \"/images/photos/image.jpg\" or \"/images/photos/image-lg.jpg\"\n // We need to check if it's under the current images path\n const thumbRelative = thumb.path.replace(/^\\/images\\/?/, '')\n \n // Check if this is directly in the current folder or in a subfolder\n if (imagesSubPath === '') {\n // We're at /images root\n const slashIndex = thumbRelative.indexOf('/')\n if (slashIndex === -1) {\n // Direct file in images root\n const fileName = thumbRelative\n items.push({\n name: fileName,\n path: `public/images/${fileName}`,\n type: 'file',\n thumbnail: thumb.path,\n hasThumbnail: false,\n isProtected: true,\n })\n } else {\n // In a subfolder - add the folder\n const folderName = thumbRelative.slice(0, slashIndex)\n if (!seenFolders.has(folderName)) {\n seenFolders.add(folderName)\n // Count thumbnails in this folder\n const folderPrefix = `/${folderName}/`\n const fileCount = allThumbnails.filter(t => \n t.path.replace(/^\\/images/, '').startsWith(folderPrefix)\n ).length\n items.push({\n name: folderName,\n path: `public/images/${folderName}`,\n type: 'folder',\n fileCount,\n isProtected: true,\n })\n }\n }\n } else {\n // We're in a subfolder of images\n if (!thumbRelative.startsWith(imagesSubPath + '/') && thumbRelative !== imagesSubPath) continue\n \n const remaining = thumbRelative.slice(imagesSubPath.length + 1)\n if (!remaining) continue\n \n const slashIndex = remaining.indexOf('/')\n if (slashIndex === -1) {\n // Direct file\n items.push({\n name: remaining,\n path: `public/images/${imagesSubPath}/${remaining}`,\n type: 'file',\n thumbnail: thumb.path,\n hasThumbnail: false,\n isProtected: true,\n })\n } else {\n // Subfolder\n const folderName = remaining.slice(0, slashIndex)\n if (!seenFolders.has(folderName)) {\n seenFolders.add(folderName)\n const folderPrefix = `${imagesSubPath}/${folderName}/`\n const fileCount = allThumbnails.filter(t => \n t.path.replace(/^\\/images\\//, '').startsWith(folderPrefix)\n ).length\n items.push({\n name: folderName,\n path: `public/images/${imagesSubPath}/${folderName}`,\n type: 'folder',\n fileCount,\n isProtected: true,\n })\n }\n }\n }\n }\n \n return NextResponse.json({ items })\n }\n \n // Not in images folder - check filesystem for folders (including empty ones)\n const absoluteDir = path.join(process.cwd(), requestedPath)\n try {\n const dirEntries = await fs.readdir(absoluteDir, { withFileTypes: true })\n for (const entry of dirEntries) {\n if (entry.name.startsWith('.')) continue\n \n if (entry.isDirectory()) {\n if (!seenFolders.has(entry.name)) {\n seenFolders.add(entry.name)\n \n // Check if this folder is the images folder\n const isImagesFolder = entry.name === 'images' && !relativePath\n const folderPath = relativePath ? `public/${relativePath}/${entry.name}` : `public/${entry.name}`\n \n // Count files in this folder\n let fileCount = 0\n if (isImagesFolder) {\n // Count thumbnails from meta for images folder\n for (const [key, metaEntry] of fileEntries) {\n if (isProcessed(metaEntry)) {\n fileCount += getExistingThumbnails(key, metaEntry).length\n }\n }\n } else {\n // Count files from meta for regular folders\n const folderPrefix = pathPrefix === '/' ? `/${entry.name}/` : `${pathPrefix}${entry.name}/`\n for (const k of metaKeys) {\n if (k.startsWith(folderPrefix)) fileCount++\n }\n }\n \n items.push({\n name: entry.name,\n path: folderPath,\n type: 'folder',\n fileCount,\n isProtected: isImagesFolder,\n })\n }\n }\n }\n } catch {\n // Directory might not exist (all files in cloud)\n }\n \n // Always show images folder at root level if any processed images exist\n if (!relativePath && !seenFolders.has('images')) {\n let thumbnailCount = 0\n for (const [key, entry] of fileEntries) {\n if (isProcessed(entry)) {\n thumbnailCount += getExistingThumbnails(key, entry).length\n }\n }\n if (thumbnailCount > 0) {\n items.push({\n name: 'images',\n path: 'public/images',\n type: 'folder',\n fileCount: thumbnailCount,\n isProtected: true,\n })\n }\n }\n \n // If meta is empty and no folders found, return empty with a flag\n if (fileEntries.length === 0 && items.length === 0) {\n return NextResponse.json({ items: [], isEmpty: true })\n }\n\n for (const [key, entry] of fileEntries) {\n // Check if this file is under the current path\n if (!key.startsWith(pathPrefix) && pathPrefix !== '/') continue\n if (pathPrefix === '/' && !key.startsWith('/')) continue\n\n // Get the part after the current path\n const remaining = pathPrefix === '/' ? key.slice(1) : key.slice(pathPrefix.length)\n \n // Skip if empty (shouldn't happen)\n if (!remaining) continue\n\n // Check if there's a subfolder\n const slashIndex = remaining.indexOf('/')\n \n if (slashIndex !== -1) {\n // This is in a subfolder - show the folder\n const folderName = remaining.slice(0, slashIndex)\n \n if (!seenFolders.has(folderName)) {\n seenFolders.add(folderName)\n \n // Count files in this folder from meta\n const folderPrefix = pathPrefix === '/' ? `/${folderName}/` : `${pathPrefix}${folderName}/`\n let fileCount = 0\n for (const k of metaKeys) {\n if (k.startsWith(folderPrefix)) fileCount++\n }\n \n items.push({\n name: folderName,\n path: relativePath ? `public/${relativePath}/${folderName}` : `public/${folderName}`,\n type: 'folder',\n fileCount,\n isProtected: isInsideImagesFolder,\n })\n }\n } else {\n // This is a file in the current folder\n const fileName = remaining\n const isImage = isImageFile(fileName)\n const isPushedToCloud = entry.c !== undefined\n \n // Determine if this is a remote import vs pushed to our R2\n const fileCdnUrl = isPushedToCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n \n let thumbnail: string | undefined\n let hasThumbnail = false\n let fileSize: number | undefined\n \n const entryIsProcessed = isProcessed(entry)\n \n if (isImage && entryIsProcessed) {\n // Has been processed - use thumbnail\n const thumbPath = getThumbnailPath(key, 'sm')\n \n if (isPushedToCloud && entry.c !== undefined) {\n // CDN thumbnail - get URL from _cdns array\n const cdnUrl = cdnUrls[entry.c]\n if (cdnUrl) {\n thumbnail = `${cdnUrl}${thumbPath}`\n hasThumbnail = true\n }\n } else {\n // Local thumbnail - check if exists\n const localThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n await fs.access(localThumbPath)\n thumbnail = thumbPath\n hasThumbnail = true\n } catch {\n // Thumbnail doesn't exist yet\n thumbnail = key\n hasThumbnail = false\n }\n }\n } else if (isImage) {\n // Not processed yet - use original (from CDN if available)\n if (isPushedToCloud && entry.c !== undefined) {\n const cdnUrl = cdnUrls[entry.c]\n thumbnail = cdnUrl ? `${cdnUrl}${key}` : key\n } else {\n thumbnail = key\n }\n hasThumbnail = false\n }\n \n // Try to get file size if file exists locally\n if (!isPushedToCloud) {\n try {\n const filePath = path.join(process.cwd(), 'public', key)\n const stats = await fs.stat(filePath)\n fileSize = stats.size\n } catch {\n // File might not exist locally (synced)\n }\n }\n \n items.push({\n name: fileName,\n path: relativePath ? `public/${relativePath}/${fileName}` : `public/${fileName}`,\n type: 'file',\n size: fileSize,\n thumbnail,\n hasThumbnail,\n isProcessed: entryIsProcessed,\n cdnPushed: isPushedToCloud,\n cdnBaseUrl: fileCdnUrl,\n isRemote,\n isProtected: isInsideImagesFolder,\n dimensions: entry.o ? { width: entry.o.w, height: entry.o.h } : undefined,\n })\n }\n }\n\n return NextResponse.json({ items })\n } catch (error) {\n console.error('Failed to list directory:', error)\n return NextResponse.json({ error: 'Failed to list directory' }, { status: 500 })\n }\n}\n\nexport async function handleSearch(request: NextRequest) {\n const searchParams = request.nextUrl.searchParams\n const query = searchParams.get('q')?.toLowerCase() || ''\n \n if (query.length < 2) {\n return NextResponse.json({ items: [] })\n }\n\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n const items: FileItem[] = []\n\n for (const [key, entry] of fileEntries) {\n // Check if the path matches the query\n if (!key.toLowerCase().includes(query)) continue\n \n const fileName = path.basename(key)\n const relativePath = key.slice(1) // Remove leading /\n const isImage = isImageFile(fileName)\n const isPushedToCloud = entry.c !== undefined\n \n // Determine if this is a remote import vs pushed to our R2\n const fileCdnUrl = isPushedToCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n \n let thumbnail: string | undefined\n let hasThumbnail = false\n const entryIsProcessed = isProcessed(entry)\n \n if (isImage && entryIsProcessed) {\n // Has been processed - use thumbnail\n const thumbPath = getThumbnailPath(key, 'sm')\n \n if (isPushedToCloud && entry.c !== undefined) {\n const cdnUrl = cdnUrls[entry.c]\n if (cdnUrl) {\n thumbnail = `${cdnUrl}${thumbPath}`\n hasThumbnail = true\n }\n } else {\n const localThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n await fs.access(localThumbPath)\n thumbnail = thumbPath\n hasThumbnail = true\n } catch {\n thumbnail = key\n hasThumbnail = false\n }\n }\n } else if (isImage) {\n // Not processed yet - use original (from CDN if available)\n if (isPushedToCloud && entry.c !== undefined) {\n const cdnUrl = cdnUrls[entry.c]\n thumbnail = cdnUrl ? `${cdnUrl}${key}` : key\n } else {\n thumbnail = key\n }\n hasThumbnail = false\n }\n \n items.push({\n name: fileName,\n path: `public/${relativePath}`,\n type: 'file',\n thumbnail,\n hasThumbnail,\n isProcessed: entryIsProcessed,\n cdnPushed: isPushedToCloud,\n cdnBaseUrl: fileCdnUrl,\n isRemote,\n dimensions: entry.o ? { width: entry.o.w, height: entry.o.h } : undefined,\n })\n }\n\n return NextResponse.json({ items })\n } catch (error) {\n console.error('Failed to search:', error)\n return NextResponse.json({ error: 'Failed to search' }, { status: 500 })\n }\n}\n\nexport async function handleListFolders() {\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const folderSet = new Set<string>()\n \n // Extract all folder paths from meta keys\n for (const [key] of fileEntries) {\n const parts = key.split('/')\n // Build up folder paths: /photos/2024/image.jpg -> photos, photos/2024\n let current = ''\n for (let i = 1; i < parts.length - 1; i++) {\n current = current ? `${current}/${parts[i]}` : parts[i]\n folderSet.add(current)\n }\n }\n \n // Also scan filesystem recursively for folders (including empty ones)\n async function scanDir(dir: string, relativePath: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'images') {\n const folderRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n folderSet.add(folderRelPath)\n // Recurse into subdirectory\n await scanDir(path.join(dir, entry.name), folderRelPath)\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n \n const publicDir = path.join(process.cwd(), 'public')\n await scanDir(publicDir, '')\n \n const folders: { path: string; name: string; depth: number }[] = []\n folders.push({ path: 'public', name: 'public', depth: 0 })\n \n const sortedFolders = Array.from(folderSet).sort()\n for (const folderPath of sortedFolders) {\n const depth = folderPath.split('/').length\n const name = folderPath.split('/').pop() || folderPath\n folders.push({\n path: `public/${folderPath}`,\n name,\n depth\n })\n }\n\n return NextResponse.json({ folders })\n } catch (error) {\n console.error('Failed to list folders:', error)\n return NextResponse.json({ error: 'Failed to list folders' }, { status: 500 })\n }\n}\n\nexport async function handleCountImages() {\n try {\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const allImages: string[] = []\n\n for (const [key] of fileEntries) {\n const fileName = path.basename(key)\n if (isImageFile(fileName)) {\n allImages.push(key.slice(1)) // Remove leading /\n }\n }\n\n return NextResponse.json({\n count: allImages.length,\n images: allImages,\n })\n } catch (error) {\n console.error('Failed to count images:', error)\n return NextResponse.json({ error: 'Failed to count images' }, { status: 500 })\n }\n}\n\nexport async function handleFolderImages(request: NextRequest) {\n try {\n const searchParams = request.nextUrl.searchParams\n const foldersParam = searchParams.get('folders')\n \n if (!foldersParam) {\n return NextResponse.json({ error: 'No folders provided' }, { status: 400 })\n }\n\n const folders = foldersParam.split(',')\n const meta = await loadMeta()\n const fileEntries = getFileEntries(meta)\n const allImages: string[] = []\n\n // Convert folder paths to prefixes for matching\n const prefixes = folders.map(f => {\n const rel = f.replace(/^public\\/?/, '')\n return rel ? `/${rel}/` : '/'\n })\n\n for (const [key] of fileEntries) {\n const fileName = path.basename(key)\n if (!isImageFile(fileName)) continue\n \n // Check if this image is in one of the requested folders\n for (const prefix of prefixes) {\n if (key.startsWith(prefix) || (prefix === '/' && key.startsWith('/'))) {\n allImages.push(key.slice(1)) // Remove leading /\n break\n }\n }\n }\n\n return NextResponse.json({\n count: allImages.length,\n images: allImages,\n })\n } catch (error) {\n console.error('Failed to get folder images:', error)\n return NextResponse.json({ error: 'Failed to get folder images' }, { status: 500 })\n }\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\nimport type { FullMeta, MetaEntry } from '../../types'\n\nexport async function loadMeta(): Promise<FullMeta> {\n const metaPath = path.join(process.cwd(), '_data', '_studio.json')\n \n try {\n const content = await fs.readFile(metaPath, 'utf-8')\n return JSON.parse(content) as FullMeta\n } catch {\n return {}\n }\n}\n\nexport async function saveMeta(meta: FullMeta): Promise<void> {\n const dataDir = path.join(process.cwd(), '_data')\n await fs.mkdir(dataDir, { recursive: true })\n const metaPath = path.join(dataDir, '_studio.json')\n \n // Ensure _cdns is at the top by creating ordered object\n const ordered: FullMeta = {}\n if (meta._cdns) {\n ordered._cdns = meta._cdns\n }\n // Add all other entries\n for (const [key, value] of Object.entries(meta)) {\n if (key !== '_cdns') {\n ordered[key] = value\n }\n }\n \n await fs.writeFile(metaPath, JSON.stringify(ordered, null, 2))\n}\n\n/**\n * Get the CDN URLs array from meta\n */\nexport function getCdnUrls(meta: FullMeta): string[] {\n return meta._cdns || []\n}\n\n/**\n * Set the CDN URLs array in meta\n */\nexport function setCdnUrls(meta: FullMeta, urls: string[]): void {\n meta._cdns = urls\n}\n\n/**\n * Get or add a CDN URL, returning its index\n */\nexport function getOrAddCdnIndex(meta: FullMeta, cdnUrl: string): number {\n if (!meta._cdns) {\n meta._cdns = []\n }\n \n // Normalize URL (remove trailing slash)\n const normalizedUrl = cdnUrl.replace(/\\/$/, '')\n \n const existingIndex = meta._cdns.indexOf(normalizedUrl)\n if (existingIndex >= 0) {\n return existingIndex\n }\n \n // Add new CDN URL\n meta._cdns.push(normalizedUrl)\n return meta._cdns.length - 1\n}\n\n/**\n * Get a meta entry (excludes special keys like _cdns)\n */\nexport function getMetaEntry(meta: FullMeta, key: string): MetaEntry | undefined {\n if (key.startsWith('_')) return undefined\n const value = meta[key]\n if (Array.isArray(value)) return undefined\n return value as MetaEntry | undefined\n}\n\n/**\n * Set a meta entry\n */\nexport function setMetaEntry(meta: FullMeta, key: string, entry: MetaEntry): void {\n meta[key] = entry\n}\n\n/**\n * Delete a meta entry\n */\nexport function deleteMetaEntry(meta: FullMeta, key: string): void {\n delete meta[key]\n}\n\n/**\n * Get all file entries (excludes special keys like _cdns)\n */\nexport function getFileEntries(meta: FullMeta): Array<[string, MetaEntry]> {\n return Object.entries(meta).filter(\n ([key, value]) => !key.startsWith('_') && !Array.isArray(value)\n ) as Array<[string, MetaEntry]>\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\n\nexport function isImageFile(filename: string): boolean {\n const ext = path.extname(filename).toLowerCase()\n return ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.tiff', '.tif'].includes(ext)\n}\n\nexport function isMediaFile(filename: string): boolean {\n const ext = path.extname(filename).toLowerCase()\n // Images\n if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.tiff', '.tif'].includes(ext)) return true\n // Videos\n if (['.mp4', '.webm', '.mov', '.avi', '.mkv', '.m4v'].includes(ext)) return true\n // Audio\n if (['.mp3', '.wav', '.ogg', '.m4a', '.flac', '.aac'].includes(ext)) return true\n // Documents/PDFs\n if (['.pdf'].includes(ext)) return true\n return false\n}\n\nexport function getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase()\n switch (ext) {\n case '.jpg':\n case '.jpeg':\n return 'image/jpeg'\n case '.png':\n return 'image/png'\n case '.gif':\n return 'image/gif'\n case '.webp':\n return 'image/webp'\n case '.svg':\n return 'image/svg+xml'\n default:\n return 'application/octet-stream'\n }\n}\n\nexport async function getFolderStats(folderPath: string): Promise<{ fileCount: number; totalSize: number }> {\n let fileCount = 0\n let totalSize = 0\n\n async function scanFolder(dir: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n await scanFolder(fullPath)\n } else if (isMediaFile(entry.name)) {\n fileCount++\n const stats = await fs.stat(fullPath)\n totalSize += stats.size\n }\n }\n } catch { /* ignore errors */ }\n }\n\n await scanFolder(folderPath)\n return { fileCount, totalSize }\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\nimport sharp from 'sharp'\nimport { encode } from 'blurhash'\nimport type { MetaEntry, Dimensions } from '../../types'\n\nexport const FULL_MAX_WIDTH = 2560\n\nexport const DEFAULT_SIZES: Record<string, { width: number; suffix: string; key: 'sm' | 'md' | 'lg' }> = {\n small: { width: 300, suffix: '-sm', key: 'sm' },\n medium: { width: 700, suffix: '-md', key: 'md' },\n large: { width: 1400, suffix: '-lg', key: 'lg' },\n}\n\nexport async function processImage(\n buffer: Buffer,\n imageKey: string\n): Promise<MetaEntry> {\n const sharpInstance = sharp(buffer)\n const metadata = await sharpInstance.metadata()\n const originalWidth = metadata.width || 0\n const originalHeight = metadata.height || 0\n const ratio = originalHeight / originalWidth\n\n // Remove leading slash for path operations\n const keyWithoutSlash = imageKey.startsWith('/') ? imageKey.slice(1) : imageKey\n const baseName = path.basename(keyWithoutSlash, path.extname(keyWithoutSlash))\n const ext = path.extname(keyWithoutSlash).toLowerCase()\n const imageDir = path.dirname(keyWithoutSlash)\n\n const imagesPath = path.join(process.cwd(), 'public', 'images', imageDir === '.' ? '' : imageDir)\n await fs.mkdir(imagesPath, { recursive: true })\n\n const isPng = ext === '.png'\n const outputExt = isPng ? '.png' : '.jpg'\n\n // Build the result entry\n const entry: MetaEntry = {\n o: { w: originalWidth, h: originalHeight },\n }\n\n // Generate full size (capped at FULL_MAX_WIDTH)\n const fullFileName = imageDir === '.' ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`\n const fullPath = path.join(process.cwd(), 'public', 'images', fullFileName)\n\n let fullWidth = originalWidth\n let fullHeight = originalHeight\n\n if (originalWidth > FULL_MAX_WIDTH) {\n fullWidth = FULL_MAX_WIDTH\n fullHeight = Math.round(FULL_MAX_WIDTH * ratio)\n if (isPng) {\n await sharp(buffer).resize(fullWidth, fullHeight).png({ quality: 85 }).toFile(fullPath)\n } else {\n await sharp(buffer).resize(fullWidth, fullHeight).jpeg({ quality: 85 }).toFile(fullPath)\n }\n } else {\n if (isPng) {\n await sharp(buffer).png({ quality: 85 }).toFile(fullPath)\n } else {\n await sharp(buffer).jpeg({ quality: 85 }).toFile(fullPath)\n }\n }\n entry.f = { w: fullWidth, h: fullHeight }\n\n // Generate thumbnail sizes\n for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {\n const { width: maxWidth, suffix, key } = sizeConfig\n if (originalWidth <= maxWidth) {\n continue // Skip if original is smaller than this size\n }\n\n const newHeight = Math.round(maxWidth * ratio)\n const sizeFileName = `${baseName}${suffix}${outputExt}`\n const sizeFilePath = imageDir === '.' ? sizeFileName : `${imageDir}/${sizeFileName}`\n const sizePath = path.join(process.cwd(), 'public', 'images', sizeFilePath)\n\n if (isPng) {\n await sharp(buffer).resize(maxWidth, newHeight).png({ quality: 80 }).toFile(sizePath)\n } else {\n await sharp(buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath)\n }\n\n entry[key] = { w: maxWidth, h: newHeight }\n }\n\n // Generate blurhash\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n\n entry.b = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4)\n\n return entry\n}\n","import { promises as fs } from 'fs'\nimport path from 'path'\nimport { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'\nimport { getAllThumbnailPaths } from '../../types'\nimport { getContentType } from './files'\n\n/**\n * Purge URLs from Cloudflare cache\n * Requires CLOUDFLARE_ZONE_ID and CLOUDFLARE_API_TOKEN environment variables\n */\nexport async function purgeCloudflareCache(urls: string[]): Promise<void> {\n const zoneId = process.env.CLOUDFLARE_ZONE_ID\n const apiToken = process.env.CLOUDFLARE_API_TOKEN\n \n if (!zoneId || !apiToken || urls.length === 0) {\n return // Cache purge not configured or no URLs to purge\n }\n \n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${apiToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ files: urls }),\n }\n )\n \n if (!response.ok) {\n console.error('Cache purge failed:', await response.text())\n }\n } catch (error) {\n console.error('Cache purge error:', error)\n }\n}\n\nfunction getR2Client() {\n const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID\n const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID\n const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY\n\n if (!accountId || !accessKeyId || !secretAccessKey) {\n throw new Error('R2 not configured')\n }\n\n return new S3Client({\n region: 'auto',\n endpoint: `https://${accountId}.r2.cloudflarestorage.com`,\n credentials: { accessKeyId, secretAccessKey },\n })\n}\n\nexport async function downloadFromCdn(originalPath: string): Promise<Buffer> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n\n const response = await r2.send(\n new GetObjectCommand({\n Bucket: bucketName,\n Key: originalPath.replace(/^\\//, ''),\n })\n )\n\n const stream = response.Body as NodeJS.ReadableStream\n const chunks: Buffer[] = []\n for await (const chunk of stream) {\n chunks.push(Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\nexport async function uploadToCdn(imageKey: string): Promise<void> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n\n // Upload all thumbnail sizes derived from imageKey\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n const fileBuffer = await fs.readFile(localPath)\n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: thumbPath.replace(/^\\//, ''),\n Body: fileBuffer,\n ContentType: getContentType(thumbPath),\n })\n )\n } catch {\n // File might not exist (e.g., if image is smaller than thumbnail size)\n }\n }\n}\n\nexport async function deleteLocalThumbnails(imageKey: string): Promise<void> {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n await fs.unlink(localPath)\n } catch {\n // File might not exist\n }\n }\n}\n\n/**\n * Download image from a remote URL (not R2)\n */\nexport async function downloadFromRemoteUrl(url: string): Promise<Buffer> {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(`Failed to download from ${url}: ${response.status}`)\n }\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n}\n\n/**\n * Upload original image to R2 CDN\n */\nexport async function uploadOriginalToCdn(imageKey: string): Promise<void> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n const localPath = path.join(process.cwd(), 'public', imageKey)\n const fileBuffer = await fs.readFile(localPath)\n \n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: imageKey.replace(/^\\//, ''),\n Body: fileBuffer,\n ContentType: getContentType(imageKey),\n })\n )\n}\n\n/**\n * Delete original and thumbnails from R2 CDN\n */\nexport async function deleteFromCdn(imageKey: string, hasThumbnails: boolean): Promise<void> {\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n if (!bucketName) throw new Error('R2 bucket not configured')\n\n const r2 = getR2Client()\n\n // Delete original\n try {\n await r2.send(\n new DeleteObjectCommand({\n Bucket: bucketName,\n Key: imageKey.replace(/^\\//, ''),\n })\n )\n } catch {\n // May not exist\n }\n\n // Delete thumbnails if they exist\n if (hasThumbnails) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n try {\n await r2.send(\n new DeleteObjectCommand({\n Bucket: bucketName,\n Key: thumbPath.replace(/^\\//, ''),\n })\n )\n } catch {\n // May not exist\n }\n }\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport sharp from 'sharp'\nimport type { MetaEntry } from '../types'\nimport { getAllThumbnailPaths, isProcessed } from '../types'\nimport { \n loadMeta, \n saveMeta, \n isImageFile, \n isMediaFile,\n getCdnUrls,\n downloadFromCdn,\n downloadFromRemoteUrl,\n uploadOriginalToCdn,\n uploadToCdn,\n deleteFromCdn,\n deleteLocalThumbnails,\n processImage,\n} from './utils'\n\nexport async function handleUpload(request: NextRequest) {\n try {\n const formData = await request.formData()\n const file = formData.get('file') as File | null\n const targetPath = formData.get('path') as string || 'public'\n\n if (!file) {\n return NextResponse.json({ error: 'No file provided' }, { status: 400 })\n }\n\n const bytes = await file.arrayBuffer()\n const buffer = Buffer.from(bytes)\n\n const fileName = file.name\n const ext = path.extname(fileName).toLowerCase()\n\n const isImage = isImageFile(fileName)\n const isMedia = isMediaFile(fileName)\n\n const meta = await loadMeta()\n\n let relativeDir = ''\n if (targetPath === 'public') {\n relativeDir = ''\n } else if (targetPath.startsWith('public/')) {\n relativeDir = targetPath.replace('public/', '')\n }\n \n if (relativeDir === 'images' || relativeDir.startsWith('images/')) {\n return NextResponse.json(\n { error: 'Cannot upload to images/ folder. Upload to public/ instead - thumbnails are generated automatically.' },\n { status: 400 }\n )\n }\n\n // Build the meta key\n let imageKey = '/' + (relativeDir ? `${relativeDir}/${fileName}` : fileName)\n\n // Check for collision - rename if needed\n if (meta[imageKey]) {\n const baseName = path.basename(fileName, ext)\n let counter = 1\n let newFileName = `${baseName}-${counter}${ext}`\n let newKey = '/' + (relativeDir ? `${relativeDir}/${newFileName}` : newFileName)\n \n while (meta[newKey]) {\n counter++\n newFileName = `${baseName}-${counter}${ext}`\n newKey = '/' + (relativeDir ? `${relativeDir}/${newFileName}` : newFileName)\n }\n \n imageKey = newKey\n }\n\n // Extract actual filename from key\n const actualFileName = path.basename(imageKey)\n \n const uploadDir = path.join(process.cwd(), 'public', relativeDir)\n await fs.mkdir(uploadDir, { recursive: true })\n await fs.writeFile(path.join(uploadDir, actualFileName), buffer)\n\n if (!isMedia) {\n return NextResponse.json({ \n success: true, \n message: 'File uploaded (not a media file)',\n path: `public/${relativeDir ? relativeDir + '/' : ''}${actualFileName}`\n })\n }\n\n // Add to meta\n if (isImage && ext !== '.svg') {\n // Read dimensions for images\n try {\n const metadata = await sharp(buffer).metadata()\n meta[imageKey] = {\n o: { w: metadata.width || 0, h: metadata.height || 0 },\n }\n } catch {\n meta[imageKey] = { o: { w: 0, h: 0 } }\n }\n } else {\n // Non-image media or SVG\n meta[imageKey] = {}\n }\n\n await saveMeta(meta)\n\n return NextResponse.json({ \n success: true, \n imageKey,\n message: 'File uploaded. Run \"Process Images\" to generate thumbnails.'\n })\n } catch (error) {\n console.error('Failed to upload:', error)\n const message = error instanceof Error ? error.message : 'Unknown error'\n return NextResponse.json({ error: `Failed to upload file: ${message}` }, { status: 500 })\n }\n}\n\nexport async function handleDelete(request: NextRequest) {\n try {\n const { paths } = await request.json() as { paths: string[] }\n\n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n return NextResponse.json({ error: 'No paths provided' }, { status: 400 })\n }\n\n const meta = await loadMeta()\n const deleted: string[] = []\n const errors: string[] = []\n\n for (const itemPath of paths) {\n try {\n if (!itemPath.startsWith('public/')) {\n errors.push(`Invalid path: ${itemPath}`)\n continue\n }\n\n const absolutePath = path.join(process.cwd(), itemPath)\n const imageKey = '/' + itemPath.replace(/^public\\//, '')\n \n // Check if this is in meta (could be synced with no local file)\n const entry = meta[imageKey] as MetaEntry | undefined\n const isPushedToCloud = entry?.c !== undefined\n \n // Try to delete local file/folder\n try {\n const stats = await fs.stat(absolutePath)\n\n if (stats.isDirectory()) {\n await fs.rm(absolutePath, { recursive: true })\n \n // Remove all meta entries under this folder\n const prefix = imageKey + '/'\n for (const key of Object.keys(meta)) {\n if (key.startsWith(prefix) || key === imageKey) {\n const keyEntry = meta[key] as MetaEntry | undefined\n // Also delete local thumbnails if not synced\n if (keyEntry && keyEntry.c === undefined) {\n for (const thumbPath of getAllThumbnailPaths(key)) {\n const absoluteThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try { await fs.unlink(absoluteThumbPath) } catch { /* ignore */ }\n }\n }\n delete meta[key]\n }\n }\n } else {\n await fs.unlink(absolutePath)\n\n const isInImagesFolder = itemPath.startsWith('public/images/')\n \n if (!isInImagesFolder && entry) {\n // Delete local thumbnails if not synced\n if (!isPushedToCloud) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const absoluteThumbPath = path.join(process.cwd(), 'public', thumbPath)\n try { await fs.unlink(absoluteThumbPath) } catch { /* ignore */ }\n }\n }\n delete meta[imageKey]\n }\n }\n } catch {\n // File doesn't exist locally - might be synced\n if (entry) {\n // Just remove from meta (file is on CDN)\n delete meta[imageKey]\n } else {\n // Check if it's a folder prefix in meta\n const prefix = imageKey + '/'\n let foundAny = false\n for (const key of Object.keys(meta)) {\n if (key.startsWith(prefix)) {\n delete meta[key]\n foundAny = true\n }\n }\n if (!foundAny) {\n errors.push(`Not found: ${itemPath}`)\n continue\n }\n }\n }\n\n deleted.push(itemPath)\n } catch (error) {\n console.error(`Failed to delete ${itemPath}:`, error)\n errors.push(itemPath)\n }\n }\n\n await saveMeta(meta)\n\n return NextResponse.json({\n success: true,\n deleted,\n errors: errors.length > 0 ? errors : undefined,\n })\n } catch (error) {\n console.error('Failed to delete:', error)\n return NextResponse.json({ error: 'Failed to delete files' }, { status: 500 })\n }\n}\n\nexport async function handleCreateFolder(request: NextRequest) {\n try {\n const { parentPath, name } = await request.json()\n\n if (!name || typeof name !== 'string') {\n return NextResponse.json({ error: 'Folder name is required' }, { status: 400 })\n }\n\n const sanitizedName = name.replace(/[<>:\"/\\\\|?*]/g, '').trim()\n if (!sanitizedName) {\n return NextResponse.json({ error: 'Invalid folder name' }, { status: 400 })\n }\n\n const safePath = (parentPath || 'public').replace(/\\.\\./g, '')\n const folderPath = path.join(process.cwd(), safePath, sanitizedName)\n\n if (!folderPath.startsWith(path.join(process.cwd(), 'public'))) {\n return NextResponse.json({ error: 'Invalid path' }, { status: 400 })\n }\n\n try {\n await fs.access(folderPath)\n return NextResponse.json({ error: 'A folder with this name already exists' }, { status: 400 })\n } catch {\n // Good - folder doesn't exist\n }\n\n await fs.mkdir(folderPath, { recursive: true })\n\n return NextResponse.json({ success: true, path: path.join(safePath, sanitizedName) })\n } catch (error) {\n console.error('Failed to create folder:', error)\n return NextResponse.json({ error: 'Failed to create folder' }, { status: 500 })\n }\n}\n\nexport async function handleRename(request: NextRequest) {\n try {\n const { oldPath, newName } = await request.json()\n\n if (!oldPath || !newName) {\n return NextResponse.json({ error: 'Path and new name are required' }, { status: 400 })\n }\n\n const sanitizedName = newName.replace(/[<>:\"/\\\\|?*]/g, '').trim()\n if (!sanitizedName) {\n return NextResponse.json({ error: 'Invalid name' }, { status: 400 })\n }\n\n const safePath = oldPath.replace(/\\.\\./g, '')\n const absoluteOldPath = path.join(process.cwd(), safePath)\n const parentDir = path.dirname(absoluteOldPath)\n const absoluteNewPath = path.join(parentDir, sanitizedName)\n\n if (!absoluteOldPath.startsWith(path.join(process.cwd(), 'public'))) {\n return NextResponse.json({ error: 'Invalid path' }, { status: 400 })\n }\n\n try {\n await fs.access(absoluteOldPath)\n } catch {\n return NextResponse.json({ error: 'File or folder not found' }, { status: 404 })\n }\n\n try {\n await fs.access(absoluteNewPath)\n return NextResponse.json({ error: 'An item with this name already exists' }, { status: 400 })\n } catch {\n // Good - new path doesn't exist\n }\n\n const stats = await fs.stat(absoluteOldPath)\n const isFile = stats.isFile()\n const isImage = isFile && isImageFile(path.basename(oldPath))\n\n await fs.rename(absoluteOldPath, absoluteNewPath)\n\n if (isImage) {\n const meta = await loadMeta()\n const oldRelativePath = safePath.replace(/^public\\//, '')\n const newRelativePath = path.join(path.dirname(oldRelativePath), sanitizedName)\n const oldKey = '/' + oldRelativePath\n const newKey = '/' + newRelativePath\n\n if (meta[oldKey]) {\n const entry = meta[oldKey]\n\n const oldThumbPaths = getAllThumbnailPaths(oldKey)\n const newThumbPaths = getAllThumbnailPaths(newKey)\n\n for (let i = 0; i < oldThumbPaths.length; i++) {\n const oldThumbPath = path.join(process.cwd(), 'public', oldThumbPaths[i])\n const newThumbPath = path.join(process.cwd(), 'public', newThumbPaths[i])\n \n await fs.mkdir(path.dirname(newThumbPath), { recursive: true })\n \n try {\n await fs.rename(oldThumbPath, newThumbPath)\n } catch {\n // Thumbnail might not exist\n }\n }\n\n delete meta[oldKey]\n meta[newKey] = entry\n }\n\n await saveMeta(meta)\n }\n\n const newPath = path.join(path.dirname(safePath), sanitizedName)\n return NextResponse.json({ success: true, newPath })\n } catch (error) {\n console.error('Failed to rename:', error)\n return NextResponse.json({ error: 'Failed to rename' }, { status: 500 })\n }\n}\n\nexport async function handleMoveStream(request: NextRequest) {\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const { paths, destination } = await request.json()\n\n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n sendEvent({ type: 'error', message: 'Paths are required' })\n controller.close()\n return\n }\n\n if (!destination || typeof destination !== 'string') {\n sendEvent({ type: 'error', message: 'Destination is required' })\n controller.close()\n return\n }\n\n const safeDestination = destination.replace(/\\.\\./g, '')\n const absoluteDestination = path.join(process.cwd(), safeDestination)\n\n if (!absoluteDestination.startsWith(path.join(process.cwd(), 'public'))) {\n sendEvent({ type: 'error', message: 'Invalid destination' })\n controller.close()\n return\n }\n\n // Ensure destination folder exists\n await fs.mkdir(absoluteDestination, { recursive: true })\n\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n\n const moved: string[] = []\n const errors: string[] = []\n const total = paths.length\n\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < paths.length; i++) {\n const itemPath = paths[i]\n const safePath = itemPath.replace(/\\.\\./g, '')\n const itemName = path.basename(safePath)\n const newAbsolutePath = path.join(absoluteDestination, itemName)\n\n // Build meta keys\n const oldRelativePath = safePath.replace(/^public\\//, '')\n const newRelativePath = path.join(safeDestination.replace(/^public\\//, ''), itemName)\n const oldKey = '/' + oldRelativePath\n const newKey = '/' + newRelativePath\n\n sendEvent({\n type: 'progress',\n current: i + 1,\n total,\n percent: Math.round(((i + 1) / total) * 100),\n currentFile: itemName,\n })\n\n // Check if destination already exists in meta\n if (meta[newKey]) {\n errors.push(`${itemName} already exists in destination`)\n continue\n }\n\n const entry = meta[oldKey] as MetaEntry | undefined\n const isImage = isImageFile(itemName)\n\n // Determine if cloud or remote\n const isInCloud = entry?.c !== undefined\n const fileCdnUrl = isInCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl\n const hasProcessedThumbnails = isProcessed(entry)\n\n try {\n if (isRemote && isImage) {\n // ===== REMOTE IMAGE =====\n const remoteUrl = `${fileCdnUrl}${oldKey}`\n const buffer = await downloadFromRemoteUrl(remoteUrl)\n \n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n const newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n }\n delete meta[oldKey]\n meta[newKey] = newEntry\n moved.push(itemPath)\n\n } else if (isPushedToR2 && isImage) {\n // ===== CLOUD IMAGE (R2) =====\n const buffer = await downloadFromCdn(oldKey)\n \n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n let newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n }\n \n if (hasProcessedThumbnails) {\n const processedEntry = await processImage(buffer, newKey)\n newEntry = { ...newEntry, ...processedEntry }\n }\n \n await uploadOriginalToCdn(newKey)\n \n if (hasProcessedThumbnails) {\n await uploadToCdn(newKey)\n }\n \n await deleteFromCdn(oldKey, hasProcessedThumbnails)\n \n try { await fs.unlink(newAbsolutePath) } catch { /* ignore */ }\n if (hasProcessedThumbnails) {\n await deleteLocalThumbnails(newKey)\n }\n \n newEntry.c = entry?.c\n \n delete meta[oldKey]\n meta[newKey] = newEntry\n moved.push(itemPath)\n\n } else {\n // ===== LOCAL FILE =====\n const absolutePath = path.join(process.cwd(), safePath)\n\n if (absoluteDestination.startsWith(absolutePath + path.sep)) {\n errors.push(`Cannot move ${itemName} into itself`)\n continue\n }\n\n try {\n await fs.access(absolutePath)\n } catch {\n errors.push(`${itemName} not found`)\n continue\n }\n\n try {\n await fs.access(newAbsolutePath)\n errors.push(`${itemName} already exists in destination`)\n continue\n } catch {\n // Good\n }\n\n await fs.rename(absolutePath, newAbsolutePath)\n\n const stats = await fs.stat(newAbsolutePath)\n if (stats.isFile() && isImage && entry) {\n const oldThumbPaths = getAllThumbnailPaths(oldKey)\n const newThumbPaths = getAllThumbnailPaths(newKey)\n\n for (let j = 0; j < oldThumbPaths.length; j++) {\n const oldThumbPath = path.join(process.cwd(), 'public', oldThumbPaths[j])\n const newThumbPath = path.join(process.cwd(), 'public', newThumbPaths[j])\n \n await fs.mkdir(path.dirname(newThumbPath), { recursive: true })\n\n try {\n await fs.rename(oldThumbPath, newThumbPath)\n } catch {\n // Thumbnail might not exist\n }\n }\n\n delete meta[oldKey]\n meta[newKey] = entry\n } else if (stats.isDirectory()) {\n const oldPrefix = oldKey + '/'\n const newPrefix = newKey + '/'\n \n for (const key of Object.keys(meta)) {\n if (key.startsWith(oldPrefix)) {\n const newMetaKey = newPrefix + key.slice(oldPrefix.length)\n meta[newMetaKey] = meta[key]\n delete meta[key]\n }\n }\n }\n\n moved.push(itemPath)\n }\n } catch (err) {\n console.error(`Failed to move ${itemName}:`, err)\n errors.push(`Failed to move ${itemName}`)\n }\n }\n\n await saveMeta(meta)\n\n sendEvent({\n type: 'complete',\n moved: moved.length,\n errors: errors.length,\n errorMessages: errors,\n })\n } catch (error) {\n console.error('Failed to move:', error)\n sendEvent({ type: 'error', message: 'Failed to move items' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n\nexport async function handleMove(request: NextRequest) {\n try {\n const { paths, destination } = await request.json()\n\n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n return NextResponse.json({ error: 'Paths are required' }, { status: 400 })\n }\n\n if (!destination || typeof destination !== 'string') {\n return NextResponse.json({ error: 'Destination is required' }, { status: 400 })\n }\n\n const safeDestination = destination.replace(/\\.\\./g, '')\n const absoluteDestination = path.join(process.cwd(), safeDestination)\n\n if (!absoluteDestination.startsWith(path.join(process.cwd(), 'public'))) {\n return NextResponse.json({ error: 'Invalid destination' }, { status: 400 })\n }\n\n // Ensure destination folder exists (create if needed)\n await fs.mkdir(absoluteDestination, { recursive: true })\n\n const moved: string[] = []\n const errors: string[] = []\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const r2PublicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/$/, '') || ''\n let metaChanged = false\n\n for (const itemPath of paths) {\n const safePath = itemPath.replace(/\\.\\./g, '')\n const itemName = path.basename(safePath)\n const newAbsolutePath = path.join(absoluteDestination, itemName)\n\n // Build meta keys\n const oldRelativePath = safePath.replace(/^public\\//, '')\n const newRelativePath = path.join(safeDestination.replace(/^public\\//, ''), itemName)\n const oldKey = '/' + oldRelativePath\n const newKey = '/' + newRelativePath\n\n // Check if destination already exists in meta\n if (meta[newKey]) {\n errors.push(`${itemName} already exists in destination`)\n continue\n }\n\n const entry = meta[oldKey] as MetaEntry | undefined\n const isImage = isImageFile(itemName)\n\n // Determine if cloud or remote\n const isInCloud = entry?.c !== undefined\n const fileCdnUrl = isInCloud && entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl)\n const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl\n const hasProcessedThumbnails = isProcessed(entry)\n\n try {\n if (isRemote && isImage) {\n // ===== REMOTE IMAGE: Download from external URL, save locally, remove c =====\n const remoteUrl = `${fileCdnUrl}${oldKey}`\n const buffer = await downloadFromRemoteUrl(remoteUrl)\n \n // Save to new local location\n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n // Update meta: remove c (now local), keep other properties\n const newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n // Don't copy thumbnail dims since remote images don't have local thumbnails\n // Don't copy c since it's now local\n }\n delete meta[oldKey]\n meta[newKey] = newEntry\n metaChanged = true\n moved.push(itemPath)\n\n } else if (isPushedToR2 && isImage) {\n // ===== CLOUD IMAGE (R2): Download, move, re-upload, delete old =====\n \n // Download original from R2\n const buffer = await downloadFromCdn(oldKey)\n \n // Save to new local location\n await fs.mkdir(path.dirname(newAbsolutePath), { recursive: true })\n await fs.writeFile(newAbsolutePath, buffer)\n \n // Create new meta entry\n let newEntry: MetaEntry = {\n o: entry?.o,\n b: entry?.b,\n }\n \n // If processed, regenerate thumbnails\n if (hasProcessedThumbnails) {\n const processedEntry = await processImage(buffer, newKey)\n newEntry = { ...newEntry, ...processedEntry }\n }\n \n // Upload original to new R2 location\n await uploadOriginalToCdn(newKey)\n \n // If processed, upload thumbnails to R2\n if (hasProcessedThumbnails) {\n await uploadToCdn(newKey)\n }\n \n // Delete old files from R2\n await deleteFromCdn(oldKey, hasProcessedThumbnails)\n \n // Delete local files (keep cloud-only state)\n try { await fs.unlink(newAbsolutePath) } catch { /* ignore */ }\n if (hasProcessedThumbnails) {\n await deleteLocalThumbnails(newKey)\n }\n \n // Set c to same CDN index\n newEntry.c = entry?.c\n \n // Update meta\n delete meta[oldKey]\n meta[newKey] = newEntry\n metaChanged = true\n moved.push(itemPath)\n\n } else {\n // ===== LOCAL FILE: Use standard fs.rename =====\n const absolutePath = path.join(process.cwd(), safePath)\n\n if (absoluteDestination.startsWith(absolutePath + path.sep)) {\n errors.push(`Cannot move ${itemName} into itself`)\n continue\n }\n\n try {\n await fs.access(absolutePath)\n } catch {\n errors.push(`${itemName} not found`)\n continue\n }\n\n try {\n await fs.access(newAbsolutePath)\n errors.push(`${itemName} already exists in destination`)\n continue\n } catch {\n // Good - doesn't exist\n }\n\n await fs.rename(absolutePath, newAbsolutePath)\n\n const stats = await fs.stat(newAbsolutePath)\n if (stats.isFile() && isImage && entry) {\n // Move local thumbnails\n const oldThumbPaths = getAllThumbnailPaths(oldKey)\n const newThumbPaths = getAllThumbnailPaths(newKey)\n\n for (let i = 0; i < oldThumbPaths.length; i++) {\n const oldThumbPath = path.join(process.cwd(), 'public', oldThumbPaths[i])\n const newThumbPath = path.join(process.cwd(), 'public', newThumbPaths[i])\n \n await fs.mkdir(path.dirname(newThumbPath), { recursive: true })\n\n try {\n await fs.rename(oldThumbPath, newThumbPath)\n } catch {\n // Thumbnail might not exist\n }\n }\n\n delete meta[oldKey]\n meta[newKey] = entry\n metaChanged = true\n } else if (stats.isDirectory()) {\n // Move folder: update all meta entries under this folder\n const oldPrefix = oldKey + '/'\n const newPrefix = newKey + '/'\n \n for (const key of Object.keys(meta)) {\n if (key.startsWith(oldPrefix)) {\n const newMetaKey = newPrefix + key.slice(oldPrefix.length)\n meta[newMetaKey] = meta[key]\n delete meta[key]\n metaChanged = true\n }\n }\n }\n\n moved.push(itemPath)\n }\n } catch (err) {\n console.error(`Failed to move ${itemName}:`, err)\n errors.push(`Failed to move ${itemName}`)\n }\n }\n\n if (metaChanged) {\n await saveMeta(meta)\n }\n\n return NextResponse.json({\n success: errors.length === 0,\n moved,\n errors: errors.length > 0 ? errors : undefined\n })\n } catch (error) {\n console.error('Failed to move:', error)\n return NextResponse.json({ error: 'Failed to move items' }, { status: 500 })\n }\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'\nimport { getAllThumbnailPaths, isProcessed } from '../types'\nimport {\n loadMeta,\n saveMeta,\n isImageFile,\n getContentType,\n processImage,\n downloadFromCdn,\n uploadToCdn,\n deleteLocalThumbnails,\n getOrAddCdnIndex,\n getFileEntries,\n getMetaEntry,\n getCdnUrls,\n downloadFromRemoteUrl,\n purgeCloudflareCache,\n} from './utils'\n\nexport async function handleSync(request: NextRequest) {\n const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID\n const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID\n const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY\n const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME\n const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/\\s*$/, '')\n\n if (!accountId || !accessKeyId || !secretAccessKey || !bucketName || !publicUrl) {\n return NextResponse.json(\n { error: 'R2 not configured. Set CLOUDFLARE_R2_* environment variables.' },\n { status: 400 }\n )\n }\n\n try {\n const { imageKeys } = await request.json() as { imageKeys: string[] }\n\n if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {\n return NextResponse.json({ error: 'No image keys provided' }, { status: 400 })\n }\n\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n \n // Get or add CDN URL to the _cdns array\n const cdnIndex = getOrAddCdnIndex(meta, publicUrl)\n\n const r2 = new S3Client({\n region: 'auto',\n endpoint: `https://${accountId}.r2.cloudflarestorage.com`,\n credentials: { accessKeyId, secretAccessKey },\n })\n\n const pushed: string[] = []\n const errors: string[] = []\n const urlsToPurge: string[] = []\n\n for (let imageKey of imageKeys) {\n // Normalize key to have leading /\n if (!imageKey.startsWith('/')) {\n imageKey = `/${imageKey}`\n }\n \n const entry = getMetaEntry(meta, imageKey)\n if (!entry) {\n errors.push(`Image not found in meta: ${imageKey}. Run Scan first.`)\n continue\n }\n\n // Check if already pushed to our R2\n const existingCdnUrl = entry.c !== undefined ? cdnUrls[entry.c] : undefined\n const isAlreadyInOurR2 = existingCdnUrl === publicUrl\n \n if (isAlreadyInOurR2) {\n pushed.push(imageKey)\n continue\n }\n\n // Check if this is a remote image (in another CDN)\n const isRemote = entry.c !== undefined && existingCdnUrl !== publicUrl\n\n try {\n let originalBuffer: Buffer\n\n if (isRemote) {\n // Download from remote URL\n const remoteUrl = `${existingCdnUrl}${imageKey}`\n originalBuffer = await downloadFromRemoteUrl(remoteUrl)\n } else {\n // Read from local file\n const originalLocalPath = path.join(process.cwd(), 'public', imageKey)\n try {\n originalBuffer = await fs.readFile(originalLocalPath)\n } catch {\n errors.push(`Original file not found: ${imageKey}`)\n continue\n }\n }\n\n // Upload original to R2\n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: imageKey.replace(/^\\//, ''),\n Body: originalBuffer,\n ContentType: getContentType(imageKey),\n })\n )\n urlsToPurge.push(`${publicUrl}${imageKey}`)\n\n // Upload thumbnails (only if processed locally, not for remote imports)\n if (!isRemote && isProcessed(entry)) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try {\n const fileBuffer = await fs.readFile(localPath)\n await r2.send(\n new PutObjectCommand({\n Bucket: bucketName,\n Key: thumbPath.replace(/^\\//, ''),\n Body: fileBuffer,\n ContentType: getContentType(thumbPath),\n })\n )\n urlsToPurge.push(`${publicUrl}${thumbPath}`)\n } catch {\n // Thumbnail might not exist\n }\n }\n }\n\n entry.c = cdnIndex\n\n // Delete local files (only for non-remote, local images being pushed)\n if (!isRemote) {\n const originalLocalPath = path.join(process.cwd(), 'public', imageKey)\n \n // Delete local thumbnails\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n const localPath = path.join(process.cwd(), 'public', thumbPath)\n try { await fs.unlink(localPath) } catch { /* ignore */ }\n }\n\n // Delete local original\n try { await fs.unlink(originalLocalPath) } catch { /* ignore */ }\n }\n\n pushed.push(imageKey)\n } catch (error) {\n console.error(`Failed to push ${imageKey}:`, error)\n errors.push(`Failed to push: ${imageKey}`)\n }\n }\n\n await saveMeta(meta)\n \n // Purge Cloudflare cache for uploaded files\n if (urlsToPurge.length > 0) {\n await purgeCloudflareCache(urlsToPurge)\n }\n\n return NextResponse.json({\n success: true,\n pushed,\n errors: errors.length > 0 ? errors : undefined,\n })\n } catch (error) {\n console.error('Failed to push:', error)\n return NextResponse.json({ error: 'Failed to push to CDN' }, { status: 500 })\n }\n}\n\nexport async function handleReprocess(request: NextRequest) {\n const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/\\s*$/, '')\n \n try {\n const { imageKeys } = await request.json() as { imageKeys: string[] }\n\n if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {\n return NextResponse.json({ error: 'No image keys provided' }, { status: 400 })\n }\n\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const processed: string[] = []\n const errors: string[] = []\n const urlsToPurge: string[] = []\n\n for (let imageKey of imageKeys) {\n // Normalize key to have leading /\n if (!imageKey.startsWith('/')) {\n imageKey = `/${imageKey}`\n }\n \n try {\n let buffer: Buffer\n const entry = getMetaEntry(meta, imageKey)\n const existingCdnIndex = entry?.c\n const existingCdnUrl = existingCdnIndex !== undefined ? cdnUrls[existingCdnIndex] : undefined\n \n // Determine if this is our R2 or a remote CDN\n const isInOurR2 = existingCdnUrl === publicUrl\n const isRemote = existingCdnIndex !== undefined && !isInOurR2\n \n const originalPath = path.join(process.cwd(), 'public', imageKey)\n \n try {\n buffer = await fs.readFile(originalPath)\n } catch {\n if (isInOurR2) {\n // Download original from our R2\n buffer = await downloadFromCdn(imageKey)\n const dir = path.dirname(originalPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(originalPath, buffer)\n } else if (isRemote && existingCdnUrl) {\n // Download from remote URL\n const remoteUrl = `${existingCdnUrl}${imageKey}`\n buffer = await downloadFromRemoteUrl(remoteUrl)\n const dir = path.dirname(originalPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(originalPath, buffer)\n } else {\n throw new Error(`File not found: ${imageKey}`)\n }\n }\n\n const updatedEntry = await processImage(buffer, imageKey)\n // No need to set p flag - presence of thumbnail dims (sm/md/lg/f) indicates processed\n \n if (isInOurR2) {\n // Re-upload thumbnails to R2 and clean up local files\n updatedEntry.c = existingCdnIndex\n await uploadToCdn(imageKey)\n \n // Collect URLs to purge\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n urlsToPurge.push(`${publicUrl}${thumbPath}`)\n }\n \n await deleteLocalThumbnails(imageKey)\n // Delete local original\n try { await fs.unlink(originalPath) } catch { /* ignore */ }\n } else if (isRemote) {\n // Remote image processed locally - remove c flag, now it's local\n // Keep the original and thumbnails locally\n }\n \n meta[imageKey] = updatedEntry\n processed.push(imageKey)\n } catch (error) {\n console.error(`Failed to reprocess ${imageKey}:`, error)\n errors.push(imageKey)\n }\n }\n\n await saveMeta(meta)\n \n // Purge Cloudflare cache for re-uploaded thumbnails\n if (urlsToPurge.length > 0) {\n await purgeCloudflareCache(urlsToPurge)\n }\n\n return NextResponse.json({\n success: true,\n processed,\n errors: errors.length > 0 ? errors : undefined,\n })\n } catch (error) {\n console.error('Failed to reprocess:', error)\n return NextResponse.json({ error: 'Failed to reprocess images' }, { status: 500 })\n }\n}\n\nexport async function handleProcessAllStream() {\n const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL?.replace(/\\/\\s*$/, '')\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const meta = await loadMeta()\n const cdnUrls = getCdnUrls(meta)\n const processed: string[] = []\n const errors: string[] = []\n const orphansRemoved: string[] = []\n const urlsToPurge: string[] = []\n\n // Count images in different states\n let alreadyProcessed = 0\n\n // Get all images from meta that need processing (no p flag = not processed yet)\n const imagesToProcess: Array<{ key: string; entry: import('../types').MetaEntry }> = []\n \n for (const [key, entry] of getFileEntries(meta)) {\n const fileName = path.basename(key)\n if (!isImageFile(fileName)) continue\n \n // Check if needs processing (no thumbnail dims = not processed yet)\n if (!isProcessed(entry)) {\n imagesToProcess.push({ key, entry })\n } else {\n alreadyProcessed++\n }\n }\n\n const total = imagesToProcess.length\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < imagesToProcess.length; i++) {\n const { key, entry } = imagesToProcess[i]\n const fullPath = path.join(process.cwd(), 'public', key)\n const existingCdnIndex = entry.c\n const existingCdnUrl = existingCdnIndex !== undefined ? cdnUrls[existingCdnIndex] : undefined\n \n // Determine if this is our R2 or a remote CDN\n const isInOurR2 = existingCdnUrl === publicUrl\n const isRemote = existingCdnIndex !== undefined && !isInOurR2\n \n sendEvent({ \n type: 'progress', \n current: i + 1, \n total, \n percent: Math.round(((i + 1) / total) * 100),\n currentFile: key.slice(1) // Remove leading /\n })\n\n try {\n let buffer: Buffer\n \n // Download from appropriate source\n if (isInOurR2) {\n buffer = await downloadFromCdn(key)\n const dir = path.dirname(fullPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(fullPath, buffer)\n } else if (isRemote && existingCdnUrl) {\n const remoteUrl = `${existingCdnUrl}${key}`\n buffer = await downloadFromRemoteUrl(remoteUrl)\n const dir = path.dirname(fullPath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(fullPath, buffer)\n } else {\n buffer = await fs.readFile(fullPath)\n }\n \n const ext = path.extname(key).toLowerCase()\n const isSvg = ext === '.svg'\n\n if (isSvg) {\n const imageDir = path.dirname(key.slice(1))\n const imagesPath = path.join(process.cwd(), 'public', 'images', imageDir === '.' ? '' : imageDir)\n await fs.mkdir(imagesPath, { recursive: true })\n \n const fileName = path.basename(key)\n const destPath = path.join(imagesPath, fileName)\n await fs.writeFile(destPath, buffer)\n\n meta[key] = {\n ...entry,\n o: { w: 0, h: 0 },\n b: '',\n f: { w: 0, h: 0 }, // SVG has \"full\" to indicate processed\n }\n \n // Remote images become local after processing\n if (isRemote) {\n delete (meta[key] as import('../types').MetaEntry).c\n }\n } else {\n const processedEntry = await processImage(buffer, key)\n meta[key] = {\n ...processedEntry,\n ...(isInOurR2 ? { c: existingCdnIndex } : {}),\n }\n // Remote images become local after processing (no c)\n }\n\n // If image was in our R2, upload new thumbnails and clean up local files\n if (isInOurR2) {\n await uploadToCdn(key)\n \n // Collect URLs to purge\n for (const thumbPath of getAllThumbnailPaths(key)) {\n urlsToPurge.push(`${publicUrl}${thumbPath}`)\n }\n \n await deleteLocalThumbnails(key)\n // Delete local original\n try { await fs.unlink(fullPath) } catch { /* ignore */ }\n }\n // Remote images stay local after processing (original + thumbnails)\n\n processed.push(key.slice(1))\n } catch (error) {\n console.error(`Failed to process ${key}:`, error)\n errors.push(key.slice(1))\n }\n }\n\n sendEvent({ type: 'cleanup', message: 'Removing orphaned thumbnails...' })\n \n // Build set of expected thumbnail paths\n const trackedPaths = new Set<string>()\n for (const [imageKey, entry] of getFileEntries(meta)) {\n // Only track local thumbnails (not pushed to CDN)\n if (entry.c === undefined) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n trackedPaths.add(thumbPath)\n }\n }\n }\n\n async function findOrphans(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n \n for (const fsEntry of entries) {\n if (fsEntry.name.startsWith('.')) continue\n\n const entryFullPath = path.join(dir, fsEntry.name)\n const relPath = relativePath ? `${relativePath}/${fsEntry.name}` : fsEntry.name\n\n if (fsEntry.isDirectory()) {\n await findOrphans(entryFullPath, relPath)\n } else if (isImageFile(fsEntry.name)) {\n const publicPath = `/images/${relPath}`\n if (!trackedPaths.has(publicPath)) {\n try {\n await fs.unlink(entryFullPath)\n orphansRemoved.push(publicPath)\n } catch (err) {\n console.error(`Failed to remove orphan ${publicPath}:`, err)\n }\n }\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n const imagesDir = path.join(process.cwd(), 'public', 'images')\n try {\n await findOrphans(imagesDir)\n } catch {\n // images dir might not exist\n }\n\n async function removeEmptyDirs(dir: string): Promise<boolean> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n let isEmpty = true\n\n for (const fsEntry of entries) {\n if (fsEntry.isDirectory()) {\n const subDirEmpty = await removeEmptyDirs(path.join(dir, fsEntry.name))\n if (!subDirEmpty) isEmpty = false\n } else {\n isEmpty = false\n }\n }\n\n if (isEmpty && dir !== imagesDir) {\n await fs.rmdir(dir)\n }\n\n return isEmpty\n } catch {\n return true\n }\n }\n\n try {\n await removeEmptyDirs(imagesDir)\n } catch {\n // images dir might not exist\n }\n \n await saveMeta(meta)\n \n // Purge Cloudflare cache for re-uploaded thumbnails\n if (urlsToPurge.length > 0) {\n await purgeCloudflareCache(urlsToPurge)\n }\n\n sendEvent({ \n type: 'complete', \n processed: processed.length, \n alreadyProcessed,\n orphansRemoved: orphansRemoved.length,\n errors: errors.length,\n })\n } catch (error) {\n console.error('Failed to process all:', error)\n sendEvent({ type: 'error', message: 'Failed to process images' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport sharp from 'sharp'\nimport { encode } from 'blurhash'\nimport { loadMeta, saveMeta, isMediaFile, isImageFile, getFileEntries } from './utils'\nimport { getAllThumbnailPaths, isProcessed } from '../types'\n\n/**\n * Streaming scan handler - scans filesystem for new files not in meta\n * For images, reads dimensions (w/h)\n * Handles collisions by renaming files with -1, -2, etc.\n * Also detects orphaned files in the images folder\n */\nexport async function handleScanStream() {\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const meta = await loadMeta()\n const existingCount = Object.keys(meta).filter(k => !k.startsWith('_')).length\n const existingKeys = new Set(Object.keys(meta))\n const added: string[] = []\n const renamed: Array<{ from: string; to: string }> = []\n const errors: string[] = []\n const orphanedFiles: string[] = []\n\n // Collect all files first\n const allFiles: Array<{ relativePath: string; fullPath: string }> = []\n\n async function scanDir(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n \n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n \n const fullPath = path.join(dir, entry.name)\n const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n\n // Skip the images folder (generated thumbnails)\n if (relPath === 'images' || relPath.startsWith('images/')) continue\n\n if (entry.isDirectory()) {\n await scanDir(fullPath, relPath)\n } else if (isMediaFile(entry.name)) {\n allFiles.push({ relativePath: relPath, fullPath })\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n const publicDir = path.join(process.cwd(), 'public')\n await scanDir(publicDir)\n\n const total = allFiles.length\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < allFiles.length; i++) {\n let { relativePath, fullPath } = allFiles[i]\n let imageKey = '/' + relativePath\n \n sendEvent({ \n type: 'progress', \n current: i + 1, \n total, \n percent: Math.round(((i + 1) / total) * 100),\n currentFile: relativePath \n })\n\n // Check if already in meta\n if (existingKeys.has(imageKey)) {\n // File already tracked - skip\n continue\n }\n\n // Check for collision (path exists in meta but file is new)\n if (meta[imageKey]) {\n // Need to rename this file to avoid collision\n const ext = path.extname(relativePath)\n const baseName = relativePath.slice(0, -ext.length)\n let counter = 1\n let newKey = `/${baseName}-${counter}${ext}`\n \n while (meta[newKey]) {\n counter++\n newKey = `/${baseName}-${counter}${ext}`\n }\n \n // Rename the physical file\n const newRelativePath = `${baseName}-${counter}${ext}`\n const newFullPath = path.join(process.cwd(), 'public', newRelativePath)\n \n try {\n await fs.rename(fullPath, newFullPath)\n renamed.push({ from: relativePath, to: newRelativePath })\n relativePath = newRelativePath\n fullPath = newFullPath\n imageKey = newKey\n } catch (err) {\n console.error(`Failed to rename ${relativePath}:`, err)\n errors.push(`Failed to rename ${relativePath}`)\n continue\n }\n }\n\n try {\n const isImage = isImageFile(relativePath)\n \n if (isImage) {\n // Read dimensions and generate blurhash for images\n const ext = path.extname(relativePath).toLowerCase()\n \n if (ext === '.svg') {\n // SVGs don't have pixel dimensions in the same way\n meta[imageKey] = { o: { w: 0, h: 0 }, b: '' }\n } else {\n try {\n const buffer = await fs.readFile(fullPath)\n const metadata = await sharp(buffer).metadata()\n \n // Generate blurhash\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n \n const blurhash = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4)\n \n meta[imageKey] = {\n o: { w: metadata.width || 0, h: metadata.height || 0 },\n b: blurhash,\n }\n } catch {\n // Couldn't read dimensions\n meta[imageKey] = { o: { w: 0, h: 0 } }\n }\n }\n } else {\n // Non-image files - just add empty entry\n meta[imageKey] = {}\n }\n \n existingKeys.add(imageKey)\n added.push(imageKey)\n } catch (error) {\n console.error(`Failed to process ${relativePath}:`, error)\n errors.push(relativePath)\n }\n }\n\n // Check for orphaned files in the images folder\n sendEvent({ type: 'cleanup', message: 'Checking for orphaned thumbnails...' })\n \n // Build set of expected thumbnail paths from meta entries\n const expectedThumbnails = new Set<string>()\n const fileEntries = getFileEntries(meta)\n for (const [imageKey, entry] of fileEntries) {\n // Only track local thumbnails (not pushed to CDN)\n if (entry.c === undefined && isProcessed(entry)) {\n for (const thumbPath of getAllThumbnailPaths(imageKey)) {\n expectedThumbnails.add(thumbPath)\n }\n }\n }\n\n // Scan the images folder for orphaned files\n async function findOrphans(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n \n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n\n const fullPath = path.join(dir, entry.name)\n const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n\n if (entry.isDirectory()) {\n await findOrphans(fullPath, relPath)\n } else if (isImageFile(entry.name)) {\n const publicPath = `/images/${relPath}`\n if (!expectedThumbnails.has(publicPath)) {\n orphanedFiles.push(publicPath)\n }\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n const imagesDir = path.join(process.cwd(), 'public', 'images')\n try {\n await findOrphans(imagesDir)\n } catch {\n // images dir might not exist\n }\n\n await saveMeta(meta)\n\n sendEvent({ \n type: 'complete', \n existingCount,\n added: added.length, \n renamed: renamed.length,\n errors: errors.length,\n renamedFiles: renamed,\n orphanedFiles: orphanedFiles.length > 0 ? orphanedFiles : undefined,\n })\n } catch (error) {\n console.error('Scan failed:', error)\n sendEvent({ type: 'error', message: 'Scan failed' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n\n/**\n * Delete orphaned files from the images folder\n */\nexport async function handleDeleteOrphans(request: NextRequest) {\n try {\n const { paths } = await request.json() as { paths: string[] }\n \n if (!paths || !Array.isArray(paths) || paths.length === 0) {\n return NextResponse.json({ error: 'No paths provided' }, { status: 400 })\n }\n \n const deleted: string[] = []\n const errors: string[] = []\n \n for (const orphanPath of paths) {\n // Ensure the path is within the images folder for safety\n if (!orphanPath.startsWith('/images/')) {\n errors.push(`Invalid path: ${orphanPath}`)\n continue\n }\n \n const fullPath = path.join(process.cwd(), 'public', orphanPath)\n \n try {\n await fs.unlink(fullPath)\n deleted.push(orphanPath)\n } catch (err) {\n console.error(`Failed to delete ${orphanPath}:`, err)\n errors.push(orphanPath)\n }\n }\n \n // Clean up empty directories\n const imagesDir = path.join(process.cwd(), 'public', 'images')\n \n async function removeEmptyDirs(dir: string): Promise<boolean> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true })\n let isEmpty = true\n \n for (const entry of entries) {\n if (entry.isDirectory()) {\n const subDirEmpty = await removeEmptyDirs(path.join(dir, entry.name))\n if (!subDirEmpty) isEmpty = false\n } else {\n isEmpty = false\n }\n }\n \n if (isEmpty && dir !== imagesDir) {\n await fs.rmdir(dir)\n }\n \n return isEmpty\n } catch {\n return true\n }\n }\n \n try {\n await removeEmptyDirs(imagesDir)\n } catch {\n // images dir might not exist\n }\n \n return NextResponse.json({\n success: true,\n deleted: deleted.length,\n errors: errors.length,\n })\n } catch (error) {\n console.error('Failed to delete orphans:', error)\n return NextResponse.json({ error: 'Failed to delete orphaned files' }, { status: 500 })\n }\n}\n","import { NextRequest } from 'next/server'\nimport sharp from 'sharp'\nimport { encode } from 'blurhash'\nimport {\n loadMeta,\n saveMeta,\n getOrAddCdnIndex,\n getMetaEntry,\n setMetaEntry,\n} from './utils'\nimport type { Dimensions } from '../types'\n\n/**\n * Parse an image URL into base URL and path\n */\nfunction parseImageUrl(url: string): { base: string; path: string } {\n const parsed = new URL(url)\n // Base is protocol + host\n const base = `${parsed.protocol}//${parsed.host}`\n // Path is everything after\n const path = parsed.pathname\n return { base, path }\n}\n\n/**\n * Fetch remote image and get dimensions + blurhash\n */\nasync function processRemoteImage(url: string): Promise<{ o: Dimensions; b: string }> {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(`Failed to fetch: ${response.status}`)\n }\n \n const buffer = Buffer.from(await response.arrayBuffer())\n \n const metadata = await sharp(buffer).metadata()\n \n // Generate blurhash\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n \n const blurhash = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4)\n \n return {\n o: { w: metadata.width || 0, h: metadata.height || 0 },\n b: blurhash,\n }\n}\n\n/**\n * Streaming endpoint to import images from URLs\n */\nexport async function handleImportUrls(request: NextRequest) {\n const encoder = new TextEncoder()\n \n const stream = new ReadableStream({\n async start(controller) {\n const sendEvent = (data: object) => {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`))\n }\n\n try {\n const { urls } = await request.json() as { urls: string[] }\n \n if (!urls || !Array.isArray(urls) || urls.length === 0) {\n sendEvent({ type: 'error', message: 'No URLs provided' })\n controller.close()\n return\n }\n\n const meta = await loadMeta()\n const added: string[] = []\n const skipped: string[] = []\n const errors: string[] = []\n\n const total = urls.length\n sendEvent({ type: 'start', total })\n\n for (let i = 0; i < urls.length; i++) {\n const url = urls[i].trim()\n if (!url) continue\n \n sendEvent({\n type: 'progress',\n current: i + 1,\n total,\n percent: Math.round(((i + 1) / total) * 100),\n currentFile: url,\n })\n\n try {\n // Parse URL to get base and path\n const { base, path } = parseImageUrl(url)\n \n // Check if this path already exists in meta\n const existingEntry = getMetaEntry(meta, path)\n if (existingEntry) {\n skipped.push(path)\n continue\n }\n \n // Get or add CDN URL to _cdns array\n const cdnIndex = getOrAddCdnIndex(meta, base)\n \n // Fetch and process the image\n const imageData = await processRemoteImage(url)\n \n // Add entry to meta\n // Note: No thumbnail dims since this is an external image, not processed locally\n setMetaEntry(meta, path, {\n o: imageData.o,\n b: imageData.b,\n c: cdnIndex,\n })\n \n added.push(path)\n } catch (error) {\n console.error(`Failed to import ${url}:`, error)\n errors.push(url)\n }\n }\n\n await saveMeta(meta)\n\n sendEvent({\n type: 'complete',\n added: added.length,\n skipped: skipped.length,\n errors: errors.length,\n })\n } catch (error) {\n console.error('Import failed:', error)\n sendEvent({ type: 'error', message: 'Import failed' })\n } finally {\n controller.close()\n }\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n}\n\n/**\n * Get CDN URLs for settings\n */\nexport async function handleGetCdns() {\n try {\n const meta = await loadMeta()\n const cdns = meta._cdns || []\n \n return Response.json({ cdns })\n } catch (error) {\n console.error('Failed to get CDNs:', error)\n return Response.json({ error: 'Failed to get CDNs' }, { status: 500 })\n }\n}\n\n/**\n * Update CDN URLs from settings\n */\nexport async function handleUpdateCdns(request: NextRequest) {\n try {\n const { cdns } = await request.json() as { cdns: string[] }\n \n if (!Array.isArray(cdns)) {\n return Response.json({ error: 'Invalid CDN array' }, { status: 400 })\n }\n \n const meta = await loadMeta()\n \n // Normalize URLs (remove trailing slashes)\n meta._cdns = cdns.map(url => url.replace(/\\/$/, ''))\n \n await saveMeta(meta)\n \n return Response.json({ success: true, cdns: meta._cdns })\n } catch (error) {\n console.error('Failed to update CDNs:', error)\n return Response.json({ error: 'Failed to update CDNs' }, { status: 500 })\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAsB,gBAAAA,qBAAoB;;;ACA1C,SAAsB,oBAAoB;AAC1C,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;;;ACFjB,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AAGjB,eAAsB,WAA8B;AAClD,QAAM,WAAW,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS,cAAc;AAEjE,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,UAAU,OAAO;AACnD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,SAAS,MAA+B;AAC5D,QAAM,UAAU,KAAK,KAAK,QAAQ,IAAI,GAAG,OAAO;AAChD,QAAM,GAAG,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,WAAW,KAAK,KAAK,SAAS,cAAc;AAGlD,QAAM,UAAoB,CAAC;AAC3B,MAAI,KAAK,OAAO;AACd,YAAQ,QAAQ,KAAK;AAAA,EACvB;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,QAAQ,SAAS;AACnB,cAAQ,GAAG,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,GAAG,UAAU,UAAU,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAC/D;AAKO,SAAS,WAAW,MAA0B;AACnD,SAAO,KAAK,SAAS,CAAC;AACxB;AAYO,SAAS,iBAAiB,MAAgB,QAAwB;AACvE,MAAI,CAAC,KAAK,OAAO;AACf,SAAK,QAAQ,CAAC;AAAA,EAChB;AAGA,QAAM,gBAAgB,OAAO,QAAQ,OAAO,EAAE;AAE9C,QAAM,gBAAgB,KAAK,MAAM,QAAQ,aAAa;AACtD,MAAI,iBAAiB,GAAG;AACtB,WAAO;AAAA,EACT;AAGA,OAAK,MAAM,KAAK,aAAa;AAC7B,SAAO,KAAK,MAAM,SAAS;AAC7B;AAKO,SAAS,aAAa,MAAgB,KAAoC;AAC/E,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAChC,QAAM,QAAQ,KAAK,GAAG;AACtB,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,SAAO;AACT;AAKO,SAAS,aAAa,MAAgB,KAAa,OAAwB;AAChF,OAAK,GAAG,IAAI;AACd;AAYO,SAAS,eAAe,MAA4C;AACzE,SAAO,OAAO,QAAQ,IAAI,EAAE;AAAA,IAC1B,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,IAAI,WAAW,GAAG,KAAK,CAAC,MAAM,QAAQ,KAAK;AAAA,EAChE;AACF;;;ACpGA,OAAOC,WAAU;AAEV,SAAS,YAAY,UAA2B;AACrD,QAAM,MAAMA,MAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,SAAO,CAAC,QAAQ,SAAS,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,SAAS,MAAM,EAAE,SAAS,GAAG;AACzG;AAEO,SAAS,YAAY,UAA2B;AACrD,QAAM,MAAMA,MAAK,QAAQ,QAAQ,EAAE,YAAY;AAE/C,MAAI,CAAC,QAAQ,SAAS,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,SAAS,MAAM,EAAE,SAAS,GAAG,EAAG,QAAO;AAE9G,MAAI,CAAC,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,MAAM,EAAE,SAAS,GAAG,EAAG,QAAO;AAE5E,MAAI,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,SAAS,MAAM,EAAE,SAAS,GAAG,EAAG,QAAO;AAE5E,MAAI,CAAC,MAAM,EAAE,SAAS,GAAG,EAAG,QAAO;AACnC,SAAO;AACT;AAEO,SAAS,eAAe,UAA0B;AACvD,QAAM,MAAMA,MAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,UAAQ,KAAK;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;ACtCA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,OAAO,WAAW;AAClB,SAAS,cAAc;AAGhB,IAAM,iBAAiB;AAEvB,IAAM,gBAA4F;AAAA,EACvG,OAAO,EAAE,OAAO,KAAK,QAAQ,OAAO,KAAK,KAAK;AAAA,EAC9C,QAAQ,EAAE,OAAO,KAAK,QAAQ,OAAO,KAAK,KAAK;AAAA,EAC/C,OAAO,EAAE,OAAO,MAAM,QAAQ,OAAO,KAAK,KAAK;AACjD;AAEA,eAAsB,aACpB,QACA,UACoB;AACpB,QAAM,gBAAgB,MAAM,MAAM;AAClC,QAAM,WAAW,MAAM,cAAc,SAAS;AAC9C,QAAM,gBAAgB,SAAS,SAAS;AACxC,QAAM,iBAAiB,SAAS,UAAU;AAC1C,QAAM,QAAQ,iBAAiB;AAG/B,QAAM,kBAAkB,SAAS,WAAW,GAAG,IAAI,SAAS,MAAM,CAAC,IAAI;AACvE,QAAM,WAAWA,MAAK,SAAS,iBAAiBA,MAAK,QAAQ,eAAe,CAAC;AAC7E,QAAM,MAAMA,MAAK,QAAQ,eAAe,EAAE,YAAY;AACtD,QAAM,WAAWA,MAAK,QAAQ,eAAe;AAE7C,QAAM,aAAaA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,UAAU,aAAa,MAAM,KAAK,QAAQ;AAChG,QAAMD,IAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAE9C,QAAM,QAAQ,QAAQ;AACtB,QAAM,YAAY,QAAQ,SAAS;AAGnC,QAAM,QAAmB;AAAA,IACvB,GAAG,EAAE,GAAG,eAAe,GAAG,eAAe;AAAA,EAC3C;AAGA,QAAM,eAAe,aAAa,MAAM,GAAG,QAAQ,GAAG,SAAS,KAAK,GAAG,QAAQ,IAAI,QAAQ,GAAG,SAAS;AACvG,QAAM,WAAWC,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,UAAU,YAAY;AAE1E,MAAI,YAAY;AAChB,MAAI,aAAa;AAEjB,MAAI,gBAAgB,gBAAgB;AAClC,gBAAY;AACZ,iBAAa,KAAK,MAAM,iBAAiB,KAAK;AAC9C,QAAI,OAAO;AACT,YAAM,MAAM,MAAM,EAAE,OAAO,WAAW,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ;AAAA,IACxF,OAAO;AACL,YAAM,MAAM,MAAM,EAAE,OAAO,WAAW,UAAU,EAAE,KAAK,EAAE,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ;AAAA,IACzF;AAAA,EACF,OAAO;AACL,QAAI,OAAO;AACT,YAAM,MAAM,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ;AAAA,IAC1D,OAAO;AACL,YAAM,MAAM,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ;AAAA,IAC3D;AAAA,EACF;AACA,QAAM,IAAI,EAAE,GAAG,WAAW,GAAG,WAAW;AAGxC,aAAW,CAAC,EAAE,UAAU,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC1D,UAAM,EAAE,OAAO,UAAU,QAAQ,IAAI,IAAI;AACzC,QAAI,iBAAiB,UAAU;AAC7B;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,MAAM,WAAW,KAAK;AAC7C,UAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS;AACrD,UAAM,eAAe,aAAa,MAAM,eAAe,GAAG,QAAQ,IAAI,YAAY;AAClF,UAAM,WAAWA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,UAAU,YAAY;AAE1E,QAAI,OAAO;AACT,YAAM,MAAM,MAAM,EAAE,OAAO,UAAU,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ;AAAA,IACtF,OAAO;AACL,YAAM,MAAM,MAAM,EAAE,OAAO,UAAU,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ;AAAA,IACvF;AAEA,UAAM,GAAG,IAAI,EAAE,GAAG,UAAU,GAAG,UAAU;AAAA,EAC3C;AAGA,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,MAAM,MAAM,EACtC,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,CAAC,EAChC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,QAAM,IAAI,OAAO,IAAI,kBAAkB,IAAI,GAAG,KAAK,OAAO,KAAK,QAAQ,GAAG,CAAC;AAE3E,SAAO;AACT;;;AChGA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,SAAS,UAAU,kBAAkB,kBAAkB,2BAA2B;AAQlF,eAAsB,qBAAqB,MAA+B;AACxE,QAAM,SAAS,QAAQ,IAAI;AAC3B,QAAM,WAAW,QAAQ,IAAI;AAE7B,MAAI,CAAC,UAAU,CAAC,YAAY,KAAK,WAAW,GAAG;AAC7C;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,8CAA8C,MAAM;AAAA,MACpD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,iBAAiB,UAAU,QAAQ;AAAA,UACnC,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,MAAM,uBAAuB,MAAM,SAAS,KAAK,CAAC;AAAA,IAC5D;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,sBAAsB,KAAK;AAAA,EAC3C;AACF;AAEA,SAAS,cAAc;AACrB,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,kBAAkB,QAAQ,IAAI;AAEpC,MAAI,CAAC,aAAa,CAAC,eAAe,CAAC,iBAAiB;AAClD,UAAM,IAAI,MAAM,mBAAmB;AAAA,EACrC;AAEA,SAAO,IAAI,SAAS;AAAA,IAClB,QAAQ;AAAA,IACR,UAAU,WAAW,SAAS;AAAA,IAC9B,aAAa,EAAE,aAAa,gBAAgB;AAAA,EAC9C,CAAC;AACH;AAEA,eAAsB,gBAAgB,cAAuC;AAC3E,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,0BAA0B;AAE3D,QAAM,KAAK,YAAY;AAEvB,QAAM,WAAW,MAAM,GAAG;AAAA,IACxB,IAAI,iBAAiB;AAAA,MACnB,QAAQ;AAAA,MACR,KAAK,aAAa,QAAQ,OAAO,EAAE;AAAA,IACrC,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,SAAS;AACxB,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ;AAChC,WAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,EAChC;AACA,SAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,eAAsB,YAAY,UAAiC;AACjE,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,0BAA0B;AAE3D,QAAM,KAAK,YAAY;AAGvB,aAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,UAAM,YAAYC,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AAC9D,QAAI;AACF,YAAM,aAAa,MAAMC,IAAG,SAAS,SAAS;AAC9C,YAAM,GAAG;AAAA,QACP,IAAI,iBAAiB;AAAA,UACnB,QAAQ;AAAA,UACR,KAAK,UAAU,QAAQ,OAAO,EAAE;AAAA,UAChC,MAAM;AAAA,UACN,aAAa,eAAe,SAAS;AAAA,QACvC,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,eAAsB,sBAAsB,UAAiC;AAC3E,aAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,UAAM,YAAYD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AAC9D,QAAI;AACF,YAAMC,IAAG,OAAO,SAAS;AAAA,IAC3B,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAKA,eAAsB,sBAAsB,KAA8B;AACxE,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,2BAA2B,GAAG,KAAK,SAAS,MAAM,EAAE;AAAA,EACtE;AACA,QAAM,cAAc,MAAM,SAAS,YAAY;AAC/C,SAAO,OAAO,KAAK,WAAW;AAChC;AAKA,eAAsB,oBAAoB,UAAiC;AACzE,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,0BAA0B;AAE3D,QAAM,KAAK,YAAY;AACvB,QAAM,YAAYD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAC7D,QAAM,aAAa,MAAMC,IAAG,SAAS,SAAS;AAE9C,QAAM,GAAG;AAAA,IACP,IAAI,iBAAiB;AAAA,MACnB,QAAQ;AAAA,MACR,KAAK,SAAS,QAAQ,OAAO,EAAE;AAAA,MAC/B,MAAM;AAAA,MACN,aAAa,eAAe,QAAQ;AAAA,IACtC,CAAC;AAAA,EACH;AACF;AAKA,eAAsB,cAAc,UAAkB,eAAuC;AAC3F,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,0BAA0B;AAE3D,QAAM,KAAK,YAAY;AAGvB,MAAI;AACF,UAAM,GAAG;AAAA,MACP,IAAI,oBAAoB;AAAA,QACtB,QAAQ;AAAA,QACR,KAAK,SAAS,QAAQ,OAAO,EAAE;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,MAAI,eAAe;AACjB,eAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,UAAI;AACF,cAAM,GAAG;AAAA,UACP,IAAI,oBAAoB;AAAA,YACtB,QAAQ;AAAA,YACR,KAAK,UAAU,QAAQ,OAAO,EAAE;AAAA,UAClC,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AJ1KA,SAAS,sBAAsB,cAAsB,OAA2E;AAC9H,QAAM,aAAsE,CAAC;AAE7E,MAAI,MAAM,GAAG;AACX,eAAW,KAAK,EAAE,MAAM,iBAAiB,cAAc,MAAM,GAAG,MAAM,IAAI,CAAC;AAAA,EAC7E;AACA,MAAI,MAAM,IAAI;AACZ,eAAW,KAAK,EAAE,MAAM,iBAAiB,cAAc,IAAI,GAAG,MAAM,KAAK,CAAC;AAAA,EAC5E;AACA,MAAI,MAAM,IAAI;AACZ,eAAW,KAAK,EAAE,MAAM,iBAAiB,cAAc,IAAI,GAAG,MAAM,KAAK,CAAC;AAAA,EAC5E;AACA,MAAI,MAAM,IAAI;AACZ,eAAW,KAAK,EAAE,MAAM,iBAAiB,cAAc,IAAI,GAAG,MAAM,KAAK,CAAC;AAAA,EAC5E;AAEA,SAAO;AACT;AAMA,eAAsB,WAAW,SAAsB;AACrD,QAAM,eAAe,QAAQ,QAAQ;AACrC,QAAM,gBAAgB,aAAa,IAAI,MAAM,KAAK;AAElD,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,UAAU,WAAW,IAAI;AAC/B,UAAM,cAAc,QAAQ,IAAI,0BAA0B,QAAQ,OAAO,EAAE,KAAK;AAKhF,UAAM,eAAe,cAAc,QAAQ,cAAc,EAAE;AAC3D,UAAM,aAAa,eAAe,IAAI,YAAY,MAAM;AAExD,UAAM,QAAoB,CAAC;AAC3B,UAAM,cAAc,oBAAI,IAAY;AACpC,UAAM,WAAW,YAAY,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG;AAG/C,UAAM,uBAAuB,iBAAiB,YAAY,aAAa,WAAW,SAAS;AAG3F,QAAI,sBAAsB;AAExB,YAAM,gBAAgB,aAAa,QAAQ,cAAc,EAAE;AAC3D,YAAM,eAAe,gBAAgB,IAAI,aAAa,MAAM;AAG5D,YAAM,gBAA8F,CAAC;AAErG,iBAAW,CAAC,KAAK,KAAK,KAAK,aAAa;AACtC,YAAI,YAAY,KAAK,GAAG;AACtB,gBAAM,aAAa,sBAAsB,KAAK,KAAK;AACnD,qBAAW,SAAS,YAAY;AAC9B,0BAAc,KAAK,EAAE,GAAG,OAAO,aAAa,IAAI,CAAC;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,SAAS,eAAe;AAGjC,cAAM,gBAAgB,MAAM,KAAK,QAAQ,gBAAgB,EAAE;AAG3D,YAAI,kBAAkB,IAAI;AAExB,gBAAM,aAAa,cAAc,QAAQ,GAAG;AAC5C,cAAI,eAAe,IAAI;AAErB,kBAAM,WAAW;AACjB,kBAAM,KAAK;AAAA,cACT,MAAM;AAAA,cACN,MAAM,iBAAiB,QAAQ;AAAA,cAC/B,MAAM;AAAA,cACN,WAAW,MAAM;AAAA,cACjB,cAAc;AAAA,cACd,aAAa;AAAA,YACf,CAAC;AAAA,UACH,OAAO;AAEL,kBAAM,aAAa,cAAc,MAAM,GAAG,UAAU;AACpD,gBAAI,CAAC,YAAY,IAAI,UAAU,GAAG;AAChC,0BAAY,IAAI,UAAU;AAE1B,oBAAM,eAAe,IAAI,UAAU;AACnC,oBAAM,YAAY,cAAc;AAAA,gBAAO,OACrC,EAAE,KAAK,QAAQ,aAAa,EAAE,EAAE,WAAW,YAAY;AAAA,cACzD,EAAE;AACF,oBAAM,KAAK;AAAA,gBACT,MAAM;AAAA,gBACN,MAAM,iBAAiB,UAAU;AAAA,gBACjC,MAAM;AAAA,gBACN;AAAA,gBACA,aAAa;AAAA,cACf,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,OAAO;AAEL,cAAI,CAAC,cAAc,WAAW,gBAAgB,GAAG,KAAK,kBAAkB,cAAe;AAEvF,gBAAM,YAAY,cAAc,MAAM,cAAc,SAAS,CAAC;AAC9D,cAAI,CAAC,UAAW;AAEhB,gBAAM,aAAa,UAAU,QAAQ,GAAG;AACxC,cAAI,eAAe,IAAI;AAErB,kBAAM,KAAK;AAAA,cACT,MAAM;AAAA,cACN,MAAM,iBAAiB,aAAa,IAAI,SAAS;AAAA,cACjD,MAAM;AAAA,cACN,WAAW,MAAM;AAAA,cACjB,cAAc;AAAA,cACd,aAAa;AAAA,YACf,CAAC;AAAA,UACH,OAAO;AAEL,kBAAM,aAAa,UAAU,MAAM,GAAG,UAAU;AAChD,gBAAI,CAAC,YAAY,IAAI,UAAU,GAAG;AAChC,0BAAY,IAAI,UAAU;AAC1B,oBAAM,eAAe,GAAG,aAAa,IAAI,UAAU;AACnD,oBAAM,YAAY,cAAc;AAAA,gBAAO,OACrC,EAAE,KAAK,QAAQ,eAAe,EAAE,EAAE,WAAW,YAAY;AAAA,cAC3D,EAAE;AACF,oBAAM,KAAK;AAAA,gBACT,MAAM;AAAA,gBACN,MAAM,iBAAiB,aAAa,IAAI,UAAU;AAAA,gBAClD,MAAM;AAAA,gBACN;AAAA,gBACA,aAAa;AAAA,cACf,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO,aAAa,KAAK,EAAE,MAAM,CAAC;AAAA,IACpC;AAGA,UAAM,cAAcC,MAAK,KAAK,QAAQ,IAAI,GAAG,aAAa;AAC1D,QAAI;AACF,YAAM,aAAa,MAAMC,IAAG,QAAQ,aAAa,EAAE,eAAe,KAAK,CAAC;AACxE,iBAAW,SAAS,YAAY;AAC9B,YAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAEhC,YAAI,MAAM,YAAY,GAAG;AACvB,cAAI,CAAC,YAAY,IAAI,MAAM,IAAI,GAAG;AAChC,wBAAY,IAAI,MAAM,IAAI;AAG1B,kBAAM,iBAAiB,MAAM,SAAS,YAAY,CAAC;AACnD,kBAAM,aAAa,eAAe,UAAU,YAAY,IAAI,MAAM,IAAI,KAAK,UAAU,MAAM,IAAI;AAG/F,gBAAI,YAAY;AAChB,gBAAI,gBAAgB;AAElB,yBAAW,CAAC,KAAK,SAAS,KAAK,aAAa;AAC1C,oBAAI,YAAY,SAAS,GAAG;AAC1B,+BAAa,sBAAsB,KAAK,SAAS,EAAE;AAAA,gBACrD;AAAA,cACF;AAAA,YACF,OAAO;AAEL,oBAAM,eAAe,eAAe,MAAM,IAAI,MAAM,IAAI,MAAM,GAAG,UAAU,GAAG,MAAM,IAAI;AACxF,yBAAW,KAAK,UAAU;AACxB,oBAAI,EAAE,WAAW,YAAY,EAAG;AAAA,cAClC;AAAA,YACF;AAEA,kBAAM,KAAK;AAAA,cACT,MAAM,MAAM;AAAA,cACZ,MAAM;AAAA,cACN,MAAM;AAAA,cACN;AAAA,cACA,aAAa;AAAA,YACf,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAGA,QAAI,CAAC,gBAAgB,CAAC,YAAY,IAAI,QAAQ,GAAG;AAC/C,UAAI,iBAAiB;AACrB,iBAAW,CAAC,KAAK,KAAK,KAAK,aAAa;AACtC,YAAI,YAAY,KAAK,GAAG;AACtB,4BAAkB,sBAAsB,KAAK,KAAK,EAAE;AAAA,QACtD;AAAA,MACF;AACA,UAAI,iBAAiB,GAAG;AACtB,cAAM,KAAK;AAAA,UACT,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,WAAW;AAAA,UACX,aAAa;AAAA,QACf,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,YAAY,WAAW,KAAK,MAAM,WAAW,GAAG;AAClD,aAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAAA,IACvD;AAEA,eAAW,CAAC,KAAK,KAAK,KAAK,aAAa;AAEtC,UAAI,CAAC,IAAI,WAAW,UAAU,KAAK,eAAe,IAAK;AACvD,UAAI,eAAe,OAAO,CAAC,IAAI,WAAW,GAAG,EAAG;AAGhD,YAAM,YAAY,eAAe,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,WAAW,MAAM;AAGjF,UAAI,CAAC,UAAW;AAGhB,YAAM,aAAa,UAAU,QAAQ,GAAG;AAExC,UAAI,eAAe,IAAI;AAErB,cAAM,aAAa,UAAU,MAAM,GAAG,UAAU;AAEhD,YAAI,CAAC,YAAY,IAAI,UAAU,GAAG;AAChC,sBAAY,IAAI,UAAU;AAG1B,gBAAM,eAAe,eAAe,MAAM,IAAI,UAAU,MAAM,GAAG,UAAU,GAAG,UAAU;AACxF,cAAI,YAAY;AAChB,qBAAW,KAAK,UAAU;AACxB,gBAAI,EAAE,WAAW,YAAY,EAAG;AAAA,UAClC;AAEA,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN,MAAM,eAAe,UAAU,YAAY,IAAI,UAAU,KAAK,UAAU,UAAU;AAAA,YAClF,MAAM;AAAA,YACN;AAAA,YACA,aAAa;AAAA,UACf,CAAC;AAAA,QACH;AAAA,MACF,OAAO;AAEL,cAAM,WAAW;AACjB,cAAM,UAAU,YAAY,QAAQ;AACpC,cAAM,kBAAkB,MAAM,MAAM;AAGpC,cAAM,aAAa,mBAAmB,MAAM,MAAM,SAAY,QAAQ,MAAM,CAAC,IAAI;AACjF,cAAM,WAAW,oBAAoB,CAAC,eAAe,eAAe;AAEpE,YAAI;AACJ,YAAI,eAAe;AACnB,YAAI;AAEJ,cAAM,mBAAmB,YAAY,KAAK;AAE1C,YAAI,WAAW,kBAAkB;AAE/B,gBAAM,YAAY,iBAAiB,KAAK,IAAI;AAE5C,cAAI,mBAAmB,MAAM,MAAM,QAAW;AAE5C,kBAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,gBAAI,QAAQ;AACV,0BAAY,GAAG,MAAM,GAAG,SAAS;AACjC,6BAAe;AAAA,YACjB;AAAA,UACF,OAAO;AAEL,kBAAM,iBAAiBD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AACnE,gBAAI;AACF,oBAAMC,IAAG,OAAO,cAAc;AAC9B,0BAAY;AACZ,6BAAe;AAAA,YACjB,QAAQ;AAEN,0BAAY;AACZ,6BAAe;AAAA,YACjB;AAAA,UACF;AAAA,QACF,WAAW,SAAS;AAElB,cAAI,mBAAmB,MAAM,MAAM,QAAW;AAC5C,kBAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,wBAAY,SAAS,GAAG,MAAM,GAAG,GAAG,KAAK;AAAA,UAC3C,OAAO;AACL,wBAAY;AAAA,UACd;AACA,yBAAe;AAAA,QACjB;AAGA,YAAI,CAAC,iBAAiB;AACpB,cAAI;AACF,kBAAM,WAAWD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,GAAG;AACvD,kBAAM,QAAQ,MAAMC,IAAG,KAAK,QAAQ;AACpC,uBAAW,MAAM;AAAA,UACnB,QAAQ;AAAA,UAER;AAAA,QACF;AAEA,cAAM,KAAK;AAAA,UACT,MAAM;AAAA,UACN,MAAM,eAAe,UAAU,YAAY,IAAI,QAAQ,KAAK,UAAU,QAAQ;AAAA,UAC9E,MAAM;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,aAAa;AAAA,UACb,WAAW;AAAA,UACX,YAAY;AAAA,UACZ;AAAA,UACA,aAAa;AAAA,UACb,YAAY,MAAM,IAAI,EAAE,OAAO,MAAM,EAAE,GAAG,QAAQ,MAAM,EAAE,EAAE,IAAI;AAAA,QAClE,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,aAAa,KAAK,EAAE,MAAM,CAAC;AAAA,EACpC,SAAS,OAAO;AACd,YAAQ,MAAM,6BAA6B,KAAK;AAChD,WAAO,aAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACjF;AACF;AAEA,eAAsB,aAAa,SAAsB;AACvD,QAAM,eAAe,QAAQ,QAAQ;AACrC,QAAM,QAAQ,aAAa,IAAI,GAAG,GAAG,YAAY,KAAK;AAEtD,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,aAAa,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AAAA,EACxC;AAEA,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,UAAU,WAAW,IAAI;AAC/B,UAAM,cAAc,QAAQ,IAAI,0BAA0B,QAAQ,OAAO,EAAE,KAAK;AAChF,UAAM,QAAoB,CAAC;AAE3B,eAAW,CAAC,KAAK,KAAK,KAAK,aAAa;AAEtC,UAAI,CAAC,IAAI,YAAY,EAAE,SAAS,KAAK,EAAG;AAExC,YAAM,WAAWD,MAAK,SAAS,GAAG;AAClC,YAAM,eAAe,IAAI,MAAM,CAAC;AAChC,YAAM,UAAU,YAAY,QAAQ;AACpC,YAAM,kBAAkB,MAAM,MAAM;AAGpC,YAAM,aAAa,mBAAmB,MAAM,MAAM,SAAY,QAAQ,MAAM,CAAC,IAAI;AACjF,YAAM,WAAW,oBAAoB,CAAC,eAAe,eAAe;AAEpE,UAAI;AACJ,UAAI,eAAe;AACnB,YAAM,mBAAmB,YAAY,KAAK;AAE1C,UAAI,WAAW,kBAAkB;AAE/B,cAAM,YAAY,iBAAiB,KAAK,IAAI;AAE5C,YAAI,mBAAmB,MAAM,MAAM,QAAW;AAC5C,gBAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,cAAI,QAAQ;AACV,wBAAY,GAAG,MAAM,GAAG,SAAS;AACjC,2BAAe;AAAA,UACjB;AAAA,QACF,OAAO;AACL,gBAAM,iBAAiBA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AACnE,cAAI;AACF,kBAAMC,IAAG,OAAO,cAAc;AAC9B,wBAAY;AACZ,2BAAe;AAAA,UACjB,QAAQ;AACN,wBAAY;AACZ,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF,WAAW,SAAS;AAElB,YAAI,mBAAmB,MAAM,MAAM,QAAW;AAC5C,gBAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,sBAAY,SAAS,GAAG,MAAM,GAAG,GAAG,KAAK;AAAA,QAC3C,OAAO;AACL,sBAAY;AAAA,QACd;AACA,uBAAe;AAAA,MACjB;AAEA,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,MAAM,UAAU,YAAY;AAAA,QAC5B,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb,WAAW;AAAA,QACX,YAAY;AAAA,QACZ;AAAA,QACA,YAAY,MAAM,IAAI,EAAE,OAAO,MAAM,EAAE,GAAG,QAAQ,MAAM,EAAE,EAAE,IAAI;AAAA,MAClE,CAAC;AAAA,IACH;AAEA,WAAO,aAAa,KAAK,EAAE,MAAM,CAAC;AAAA,EACpC,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzE;AACF;AAEA,eAAsB,oBAAoB;AACxC,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,YAAY,oBAAI,IAAY;AAGlC,eAAW,CAAC,GAAG,KAAK,aAAa;AAC/B,YAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,UAAI,UAAU;AACd,eAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,kBAAU,UAAU,GAAG,OAAO,IAAI,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;AACtD,kBAAU,IAAI,OAAO;AAAA,MACvB;AAAA,IACF;AAGA,mBAAe,QAAQ,KAAa,cAAqC;AACvE,UAAI;AACF,cAAM,UAAU,MAAMA,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC7D,mBAAW,SAAS,SAAS;AAC3B,cAAI,MAAM,YAAY,KAAK,CAAC,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,UAAU;AACjF,kBAAM,gBAAgB,eAAe,GAAG,YAAY,IAAI,MAAM,IAAI,KAAK,MAAM;AAC7E,sBAAU,IAAI,aAAa;AAE3B,kBAAM,QAAQD,MAAK,KAAK,KAAK,MAAM,IAAI,GAAG,aAAa;AAAA,UACzD;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,YAAYA,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AACnD,UAAM,QAAQ,WAAW,EAAE;AAE3B,UAAM,UAA2D,CAAC;AAClE,YAAQ,KAAK,EAAE,MAAM,UAAU,MAAM,UAAU,OAAO,EAAE,CAAC;AAEzD,UAAM,gBAAgB,MAAM,KAAK,SAAS,EAAE,KAAK;AACjD,eAAW,cAAc,eAAe;AACtC,YAAM,QAAQ,WAAW,MAAM,GAAG,EAAE;AACpC,YAAM,OAAO,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK;AAC5C,cAAQ,KAAK;AAAA,QACX,MAAM,UAAU,UAAU;AAAA,QAC1B;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,aAAa,KAAK,EAAE,QAAQ,CAAC;AAAA,EACtC,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO,aAAa,KAAK,EAAE,OAAO,yBAAyB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/E;AACF;AAEA,eAAsB,oBAAoB;AACxC,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,YAAsB,CAAC;AAE7B,eAAW,CAAC,GAAG,KAAK,aAAa;AAC/B,YAAM,WAAWA,MAAK,SAAS,GAAG;AAClC,UAAI,YAAY,QAAQ,GAAG;AACzB,kBAAU,KAAK,IAAI,MAAM,CAAC,CAAC;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO,aAAa,KAAK;AAAA,MACvB,OAAO,UAAU;AAAA,MACjB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO,aAAa,KAAK,EAAE,OAAO,yBAAyB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/E;AACF;AAEA,eAAsB,mBAAmB,SAAsB;AAC7D,MAAI;AACF,UAAM,eAAe,QAAQ,QAAQ;AACrC,UAAM,eAAe,aAAa,IAAI,SAAS;AAE/C,QAAI,CAAC,cAAc;AACjB,aAAO,aAAa,KAAK,EAAE,OAAO,sBAAsB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5E;AAEA,UAAM,UAAU,aAAa,MAAM,GAAG;AACtC,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,YAAsB,CAAC;AAG7B,UAAM,WAAW,QAAQ,IAAI,OAAK;AAChC,YAAM,MAAM,EAAE,QAAQ,cAAc,EAAE;AACtC,aAAO,MAAM,IAAI,GAAG,MAAM;AAAA,IAC5B,CAAC;AAED,eAAW,CAAC,GAAG,KAAK,aAAa;AAC/B,YAAM,WAAWA,MAAK,SAAS,GAAG;AAClC,UAAI,CAAC,YAAY,QAAQ,EAAG;AAG5B,iBAAW,UAAU,UAAU;AAC7B,YAAI,IAAI,WAAW,MAAM,KAAM,WAAW,OAAO,IAAI,WAAW,GAAG,GAAI;AACrE,oBAAU,KAAK,IAAI,MAAM,CAAC,CAAC;AAC3B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,aAAa,KAAK;AAAA,MACvB,OAAO,UAAU;AAAA,MACjB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,gCAAgC,KAAK;AACnD,WAAO,aAAa,KAAK,EAAE,OAAO,8BAA8B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpF;AACF;;;AK5iBA,SAAsB,gBAAAE,qBAAoB;AAC1C,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAkBlB,eAAsB,aAAa,SAAsB;AACvD,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,SAAS;AACxC,UAAM,OAAO,SAAS,IAAI,MAAM;AAChC,UAAM,aAAa,SAAS,IAAI,MAAM,KAAe;AAErD,QAAI,CAAC,MAAM;AACT,aAAOC,cAAa,KAAK,EAAE,OAAO,mBAAmB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACzE;AAEA,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,UAAM,SAAS,OAAO,KAAK,KAAK;AAEhC,UAAM,WAAW,KAAK;AACtB,UAAM,MAAMC,MAAK,QAAQ,QAAQ,EAAE,YAAY;AAE/C,UAAM,UAAU,YAAY,QAAQ;AACpC,UAAM,UAAU,YAAY,QAAQ;AAEpC,UAAM,OAAO,MAAM,SAAS;AAE5B,QAAI,cAAc;AAClB,QAAI,eAAe,UAAU;AAC3B,oBAAc;AAAA,IAChB,WAAW,WAAW,WAAW,SAAS,GAAG;AAC3C,oBAAc,WAAW,QAAQ,WAAW,EAAE;AAAA,IAChD;AAEA,QAAI,gBAAgB,YAAY,YAAY,WAAW,SAAS,GAAG;AACjE,aAAOD,cAAa;AAAA,QAClB,EAAE,OAAO,uGAAuG;AAAA,QAChH,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAGA,QAAI,WAAW,OAAO,cAAc,GAAG,WAAW,IAAI,QAAQ,KAAK;AAGnE,QAAI,KAAK,QAAQ,GAAG;AAClB,YAAM,WAAWC,MAAK,SAAS,UAAU,GAAG;AAC5C,UAAI,UAAU;AACd,UAAI,cAAc,GAAG,QAAQ,IAAI,OAAO,GAAG,GAAG;AAC9C,UAAI,SAAS,OAAO,cAAc,GAAG,WAAW,IAAI,WAAW,KAAK;AAEpE,aAAO,KAAK,MAAM,GAAG;AACnB;AACA,sBAAc,GAAG,QAAQ,IAAI,OAAO,GAAG,GAAG;AAC1C,iBAAS,OAAO,cAAc,GAAG,WAAW,IAAI,WAAW,KAAK;AAAA,MAClE;AAEA,iBAAW;AAAA,IACb;AAGA,UAAM,iBAAiBA,MAAK,SAAS,QAAQ;AAE7C,UAAM,YAAYA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,WAAW;AAChE,UAAMC,IAAG,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,UAAMA,IAAG,UAAUD,MAAK,KAAK,WAAW,cAAc,GAAG,MAAM;AAE/D,QAAI,CAAC,SAAS;AACZ,aAAOD,cAAa,KAAK;AAAA,QACvB,SAAS;AAAA,QACT,SAAS;AAAA,QACT,MAAM,UAAU,cAAc,cAAc,MAAM,EAAE,GAAG,cAAc;AAAA,MACvE,CAAC;AAAA,IACH;AAGA,QAAI,WAAW,QAAQ,QAAQ;AAE7B,UAAI;AACF,cAAM,WAAW,MAAMG,OAAM,MAAM,EAAE,SAAS;AAC9C,aAAK,QAAQ,IAAI;AAAA,UACf,GAAG,EAAE,GAAG,SAAS,SAAS,GAAG,GAAG,SAAS,UAAU,EAAE;AAAA,QACvD;AAAA,MACF,QAAQ;AACN,aAAK,QAAQ,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,EAAE;AAAA,MACvC;AAAA,IACF,OAAO;AAEL,WAAK,QAAQ,IAAI,CAAC;AAAA,IACpB;AAEA,UAAM,SAAS,IAAI;AAEnB,WAAOH,cAAa,KAAK;AAAA,MACvB,SAAS;AAAA,MACT;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAOA,cAAa,KAAK,EAAE,OAAO,0BAA0B,OAAO,GAAG,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1F;AACF;AAEA,eAAsB,aAAa,SAAsB;AACvD,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,QAAQ,KAAK;AAErC,QAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AACzD,aAAOA,cAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1E;AAEA,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,UAAoB,CAAC;AAC3B,UAAM,SAAmB,CAAC;AAE1B,eAAW,YAAY,OAAO;AAC5B,UAAI;AACF,YAAI,CAAC,SAAS,WAAW,SAAS,GAAG;AACnC,iBAAO,KAAK,iBAAiB,QAAQ,EAAE;AACvC;AAAA,QACF;AAEA,cAAM,eAAeC,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AACtD,cAAM,WAAW,MAAM,SAAS,QAAQ,aAAa,EAAE;AAGvD,cAAM,QAAQ,KAAK,QAAQ;AAC3B,cAAM,kBAAkB,OAAO,MAAM;AAGrC,YAAI;AACF,gBAAM,QAAQ,MAAMC,IAAG,KAAK,YAAY;AAExC,cAAI,MAAM,YAAY,GAAG;AACvB,kBAAMA,IAAG,GAAG,cAAc,EAAE,WAAW,KAAK,CAAC;AAG7C,kBAAM,SAAS,WAAW;AAC1B,uBAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,kBAAI,IAAI,WAAW,MAAM,KAAK,QAAQ,UAAU;AAC9C,sBAAM,WAAW,KAAK,GAAG;AAEzB,oBAAI,YAAY,SAAS,MAAM,QAAW;AACxC,6BAAW,aAAa,qBAAqB,GAAG,GAAG;AACjD,0BAAM,oBAAoBD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AACtE,wBAAI;AAAE,4BAAMC,IAAG,OAAO,iBAAiB;AAAA,oBAAE,QAAQ;AAAA,oBAAe;AAAA,kBAClE;AAAA,gBACF;AACA,uBAAO,KAAK,GAAG;AAAA,cACjB;AAAA,YACF;AAAA,UACF,OAAO;AACL,kBAAMA,IAAG,OAAO,YAAY;AAE5B,kBAAM,mBAAmB,SAAS,WAAW,gBAAgB;AAE7D,gBAAI,CAAC,oBAAoB,OAAO;AAE9B,kBAAI,CAAC,iBAAiB;AACpB,2BAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,wBAAM,oBAAoBD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AACtE,sBAAI;AAAE,0BAAMC,IAAG,OAAO,iBAAiB;AAAA,kBAAE,QAAQ;AAAA,kBAAe;AAAA,gBAClE;AAAA,cACF;AACA,qBAAO,KAAK,QAAQ;AAAA,YACtB;AAAA,UACF;AAAA,QACF,QAAQ;AAEN,cAAI,OAAO;AAET,mBAAO,KAAK,QAAQ;AAAA,UACtB,OAAO;AAEL,kBAAM,SAAS,WAAW;AAC1B,gBAAI,WAAW;AACf,uBAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,kBAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,uBAAO,KAAK,GAAG;AACf,2BAAW;AAAA,cACb;AAAA,YACF;AACA,gBAAI,CAAC,UAAU;AACb,qBAAO,KAAK,cAAc,QAAQ,EAAE;AACpC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,gBAAQ,KAAK,QAAQ;AAAA,MACvB,SAAS,OAAO;AACd,gBAAQ,MAAM,oBAAoB,QAAQ,KAAK,KAAK;AACpD,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,SAAS,IAAI;AAEnB,WAAOF,cAAa,KAAK;AAAA,MACvB,SAAS;AAAA,MACT;AAAA,MACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACvC,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAOA,cAAa,KAAK,EAAE,OAAO,yBAAyB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC/E;AACF;AAEA,eAAsB,mBAAmB,SAAsB;AAC7D,MAAI;AACF,UAAM,EAAE,YAAY,KAAK,IAAI,MAAM,QAAQ,KAAK;AAEhD,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,aAAOA,cAAa,KAAK,EAAE,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAChF;AAEA,UAAM,gBAAgB,KAAK,QAAQ,iBAAiB,EAAE,EAAE,KAAK;AAC7D,QAAI,CAAC,eAAe;AAClB,aAAOA,cAAa,KAAK,EAAE,OAAO,sBAAsB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5E;AAEA,UAAM,YAAY,cAAc,UAAU,QAAQ,SAAS,EAAE;AAC7D,UAAM,aAAaC,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,aAAa;AAEnE,QAAI,CAAC,WAAW,WAAWA,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,CAAC,GAAG;AAC9D,aAAOD,cAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AAEA,QAAI;AACF,YAAME,IAAG,OAAO,UAAU;AAC1B,aAAOF,cAAa,KAAK,EAAE,OAAO,yCAAyC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/F,QAAQ;AAAA,IAER;AAEA,UAAME,IAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAE9C,WAAOF,cAAa,KAAK,EAAE,SAAS,MAAM,MAAMC,MAAK,KAAK,UAAU,aAAa,EAAE,CAAC;AAAA,EACtF,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,KAAK;AAC/C,WAAOD,cAAa,KAAK,EAAE,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAChF;AACF;AAEA,eAAsB,aAAa,SAAsB;AACvD,MAAI;AACF,UAAM,EAAE,SAAS,QAAQ,IAAI,MAAM,QAAQ,KAAK;AAEhD,QAAI,CAAC,WAAW,CAAC,SAAS;AACxB,aAAOA,cAAa,KAAK,EAAE,OAAO,iCAAiC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACvF;AAEA,UAAM,gBAAgB,QAAQ,QAAQ,iBAAiB,EAAE,EAAE,KAAK;AAChE,QAAI,CAAC,eAAe;AAClB,aAAOA,cAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AAEA,UAAM,WAAW,QAAQ,QAAQ,SAAS,EAAE;AAC5C,UAAM,kBAAkBC,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AACzD,UAAM,YAAYA,MAAK,QAAQ,eAAe;AAC9C,UAAM,kBAAkBA,MAAK,KAAK,WAAW,aAAa;AAE1D,QAAI,CAAC,gBAAgB,WAAWA,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,CAAC,GAAG;AACnE,aAAOD,cAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AAEA,QAAI;AACF,YAAME,IAAG,OAAO,eAAe;AAAA,IACjC,QAAQ;AACN,aAAOF,cAAa,KAAK,EAAE,OAAO,2BAA2B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjF;AAEA,QAAI;AACF,YAAME,IAAG,OAAO,eAAe;AAC/B,aAAOF,cAAa,KAAK,EAAE,OAAO,wCAAwC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F,QAAQ;AAAA,IAER;AAEA,UAAM,QAAQ,MAAME,IAAG,KAAK,eAAe;AAC3C,UAAM,SAAS,MAAM,OAAO;AAC5B,UAAM,UAAU,UAAU,YAAYD,MAAK,SAAS,OAAO,CAAC;AAE5D,UAAMC,IAAG,OAAO,iBAAiB,eAAe;AAEhD,QAAI,SAAS;AACX,YAAM,OAAO,MAAM,SAAS;AAC5B,YAAM,kBAAkB,SAAS,QAAQ,aAAa,EAAE;AACxD,YAAM,kBAAkBD,MAAK,KAAKA,MAAK,QAAQ,eAAe,GAAG,aAAa;AAC9E,YAAM,SAAS,MAAM;AACrB,YAAM,SAAS,MAAM;AAErB,UAAI,KAAK,MAAM,GAAG;AAChB,cAAM,QAAQ,KAAK,MAAM;AAEzB,cAAM,gBAAgB,qBAAqB,MAAM;AACjD,cAAM,gBAAgB,qBAAqB,MAAM;AAEjD,iBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,gBAAM,eAAeA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,cAAc,CAAC,CAAC;AACxE,gBAAM,eAAeA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,cAAc,CAAC,CAAC;AAExE,gBAAMC,IAAG,MAAMD,MAAK,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9D,cAAI;AACF,kBAAMC,IAAG,OAAO,cAAc,YAAY;AAAA,UAC5C,QAAQ;AAAA,UAER;AAAA,QACF;AAEA,eAAO,KAAK,MAAM;AAClB,aAAK,MAAM,IAAI;AAAA,MACjB;AAEA,YAAM,SAAS,IAAI;AAAA,IACrB;AAEA,UAAM,UAAUD,MAAK,KAAKA,MAAK,QAAQ,QAAQ,GAAG,aAAa;AAC/D,WAAOD,cAAa,KAAK,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EACrD,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAOA,cAAa,KAAK,EAAE,OAAO,mBAAmB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzE;AACF;AAEA,eAAsB,iBAAiB,SAAsB;AAC3D,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,MAAM,YAAY;AACtB,YAAM,YAAY,CAAC,SAAiB;AAClC,mBAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM,CAAC;AAAA,MACxE;AAEA,UAAI;AACF,cAAM,EAAE,OAAO,YAAY,IAAI,MAAM,QAAQ,KAAK;AAElD,YAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AACzD,oBAAU,EAAE,MAAM,SAAS,SAAS,qBAAqB,CAAC;AAC1D,qBAAW,MAAM;AACjB;AAAA,QACF;AAEA,YAAI,CAAC,eAAe,OAAO,gBAAgB,UAAU;AACnD,oBAAU,EAAE,MAAM,SAAS,SAAS,0BAA0B,CAAC;AAC/D,qBAAW,MAAM;AACjB;AAAA,QACF;AAEA,cAAM,kBAAkB,YAAY,QAAQ,SAAS,EAAE;AACvD,cAAM,sBAAsBC,MAAK,KAAK,QAAQ,IAAI,GAAG,eAAe;AAEpE,YAAI,CAAC,oBAAoB,WAAWA,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,CAAC,GAAG;AACvE,oBAAU,EAAE,MAAM,SAAS,SAAS,sBAAsB,CAAC;AAC3D,qBAAW,MAAM;AACjB;AAAA,QACF;AAGA,cAAMC,IAAG,MAAM,qBAAqB,EAAE,WAAW,KAAK,CAAC;AAEvD,cAAM,OAAO,MAAM,SAAS;AAC5B,cAAM,UAAU,WAAW,IAAI;AAC/B,cAAM,cAAc,QAAQ,IAAI,0BAA0B,QAAQ,OAAO,EAAE,KAAK;AAEhF,cAAM,QAAkB,CAAC;AACzB,cAAM,SAAmB,CAAC;AAC1B,cAAM,QAAQ,MAAM;AAEpB,kBAAU,EAAE,MAAM,SAAS,MAAM,CAAC;AAElC,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,WAAW,MAAM,CAAC;AACxB,gBAAM,WAAW,SAAS,QAAQ,SAAS,EAAE;AAC7C,gBAAM,WAAWD,MAAK,SAAS,QAAQ;AACvC,gBAAM,kBAAkBA,MAAK,KAAK,qBAAqB,QAAQ;AAG/D,gBAAM,kBAAkB,SAAS,QAAQ,aAAa,EAAE;AACxD,gBAAM,kBAAkBA,MAAK,KAAK,gBAAgB,QAAQ,aAAa,EAAE,GAAG,QAAQ;AACpF,gBAAM,SAAS,MAAM;AACrB,gBAAM,SAAS,MAAM;AAErB,oBAAU;AAAA,YACR,MAAM;AAAA,YACN,SAAS,IAAI;AAAA,YACb;AAAA,YACA,SAAS,KAAK,OAAQ,IAAI,KAAK,QAAS,GAAG;AAAA,YAC3C,aAAa;AAAA,UACf,CAAC;AAGD,cAAI,KAAK,MAAM,GAAG;AAChB,mBAAO,KAAK,GAAG,QAAQ,gCAAgC;AACvD;AAAA,UACF;AAEA,gBAAM,QAAQ,KAAK,MAAM;AACzB,gBAAM,UAAU,YAAY,QAAQ;AAGpC,gBAAM,YAAY,OAAO,MAAM;AAC/B,gBAAM,aAAa,aAAa,MAAM,MAAM,SAAY,QAAQ,MAAM,CAAC,IAAI;AAC3E,gBAAM,WAAW,cAAc,CAAC,eAAe,eAAe;AAC9D,gBAAM,eAAe,aAAa,eAAe,eAAe;AAChE,gBAAM,yBAAyB,YAAY,KAAK;AAEhD,cAAI;AACF,gBAAI,YAAY,SAAS;AAEvB,oBAAM,YAAY,GAAG,UAAU,GAAG,MAAM;AACxC,oBAAM,SAAS,MAAM,sBAAsB,SAAS;AAEpD,oBAAMC,IAAG,MAAMD,MAAK,QAAQ,eAAe,GAAG,EAAE,WAAW,KAAK,CAAC;AACjE,oBAAMC,IAAG,UAAU,iBAAiB,MAAM;AAE1C,oBAAM,WAAsB;AAAA,gBAC1B,GAAG,OAAO;AAAA,gBACV,GAAG,OAAO;AAAA,cACZ;AACA,qBAAO,KAAK,MAAM;AAClB,mBAAK,MAAM,IAAI;AACf,oBAAM,KAAK,QAAQ;AAAA,YAErB,WAAW,gBAAgB,SAAS;AAElC,oBAAM,SAAS,MAAM,gBAAgB,MAAM;AAE3C,oBAAMA,IAAG,MAAMD,MAAK,QAAQ,eAAe,GAAG,EAAE,WAAW,KAAK,CAAC;AACjE,oBAAMC,IAAG,UAAU,iBAAiB,MAAM;AAE1C,kBAAI,WAAsB;AAAA,gBACxB,GAAG,OAAO;AAAA,gBACV,GAAG,OAAO;AAAA,cACZ;AAEA,kBAAI,wBAAwB;AAC1B,sBAAM,iBAAiB,MAAM,aAAa,QAAQ,MAAM;AACxD,2BAAW,EAAE,GAAG,UAAU,GAAG,eAAe;AAAA,cAC9C;AAEA,oBAAM,oBAAoB,MAAM;AAEhC,kBAAI,wBAAwB;AAC1B,sBAAM,YAAY,MAAM;AAAA,cAC1B;AAEA,oBAAM,cAAc,QAAQ,sBAAsB;AAElD,kBAAI;AAAE,sBAAMA,IAAG,OAAO,eAAe;AAAA,cAAE,QAAQ;AAAA,cAAe;AAC9D,kBAAI,wBAAwB;AAC1B,sBAAM,sBAAsB,MAAM;AAAA,cACpC;AAEA,uBAAS,IAAI,OAAO;AAEpB,qBAAO,KAAK,MAAM;AAClB,mBAAK,MAAM,IAAI;AACf,oBAAM,KAAK,QAAQ;AAAA,YAErB,OAAO;AAEL,oBAAM,eAAeD,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AAEtD,kBAAI,oBAAoB,WAAW,eAAeA,MAAK,GAAG,GAAG;AAC3D,uBAAO,KAAK,eAAe,QAAQ,cAAc;AACjD;AAAA,cACF;AAEA,kBAAI;AACF,sBAAMC,IAAG,OAAO,YAAY;AAAA,cAC9B,QAAQ;AACN,uBAAO,KAAK,GAAG,QAAQ,YAAY;AACnC;AAAA,cACF;AAEA,kBAAI;AACF,sBAAMA,IAAG,OAAO,eAAe;AAC/B,uBAAO,KAAK,GAAG,QAAQ,gCAAgC;AACvD;AAAA,cACF,QAAQ;AAAA,cAER;AAEA,oBAAMA,IAAG,OAAO,cAAc,eAAe;AAE7C,oBAAM,QAAQ,MAAMA,IAAG,KAAK,eAAe;AAC3C,kBAAI,MAAM,OAAO,KAAK,WAAW,OAAO;AACtC,sBAAM,gBAAgB,qBAAqB,MAAM;AACjD,sBAAM,gBAAgB,qBAAqB,MAAM;AAEjD,yBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,wBAAM,eAAeD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,cAAc,CAAC,CAAC;AACxE,wBAAM,eAAeA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,cAAc,CAAC,CAAC;AAExE,wBAAMC,IAAG,MAAMD,MAAK,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9D,sBAAI;AACF,0BAAMC,IAAG,OAAO,cAAc,YAAY;AAAA,kBAC5C,QAAQ;AAAA,kBAER;AAAA,gBACF;AAEA,uBAAO,KAAK,MAAM;AAClB,qBAAK,MAAM,IAAI;AAAA,cACjB,WAAW,MAAM,YAAY,GAAG;AAC9B,sBAAM,YAAY,SAAS;AAC3B,sBAAM,YAAY,SAAS;AAE3B,2BAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,sBAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,0BAAM,aAAa,YAAY,IAAI,MAAM,UAAU,MAAM;AACzD,yBAAK,UAAU,IAAI,KAAK,GAAG;AAC3B,2BAAO,KAAK,GAAG;AAAA,kBACjB;AAAA,gBACF;AAAA,cACF;AAEA,oBAAM,KAAK,QAAQ;AAAA,YACrB;AAAA,UACF,SAAS,KAAK;AACZ,oBAAQ,MAAM,kBAAkB,QAAQ,KAAK,GAAG;AAChD,mBAAO,KAAK,kBAAkB,QAAQ,EAAE;AAAA,UAC1C;AAAA,QACF;AAEA,cAAM,SAAS,IAAI;AAEnB,kBAAU;AAAA,UACR,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,QAAQ,OAAO;AAAA,UACf,eAAe;AAAA,QACjB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,mBAAmB,KAAK;AACtC,kBAAU,EAAE,MAAM,SAAS,SAAS,uBAAuB,CAAC;AAAA,MAC9D,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,QAAQ;AAAA,IAC1B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;;;AC1jBA,SAAsB,gBAAAE,qBAAoB;AAC1C,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,SAAS,YAAAC,WAAU,oBAAAC,yBAAwB;AAmB3C,eAAsB,WAAW,SAAsB;AACrD,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,kBAAkB,QAAQ,IAAI;AACpC,QAAM,aAAa,QAAQ,IAAI;AAC/B,QAAM,YAAY,QAAQ,IAAI,0BAA0B,QAAQ,UAAU,EAAE;AAE5E,MAAI,CAAC,aAAa,CAAC,eAAe,CAAC,mBAAmB,CAAC,cAAc,CAAC,WAAW;AAC/E,WAAOC,cAAa;AAAA,MAClB,EAAE,OAAO,gEAAgE;AAAA,MACzE,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,QAAQ,KAAK;AAEzC,QAAI,CAAC,aAAa,CAAC,MAAM,QAAQ,SAAS,KAAK,UAAU,WAAW,GAAG;AACrE,aAAOA,cAAa,KAAK,EAAE,OAAO,yBAAyB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/E;AAEA,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,UAAU,WAAW,IAAI;AAG/B,UAAM,WAAW,iBAAiB,MAAM,SAAS;AAEjD,UAAM,KAAK,IAAIC,UAAS;AAAA,MACtB,QAAQ;AAAA,MACR,UAAU,WAAW,SAAS;AAAA,MAC9B,aAAa,EAAE,aAAa,gBAAgB;AAAA,IAC9C,CAAC;AAED,UAAM,SAAmB,CAAC;AAC1B,UAAM,SAAmB,CAAC;AAC1B,UAAM,cAAwB,CAAC;AAE/B,aAAS,YAAY,WAAW;AAE9B,UAAI,CAAC,SAAS,WAAW,GAAG,GAAG;AAC7B,mBAAW,IAAI,QAAQ;AAAA,MACzB;AAEA,YAAM,QAAQ,aAAa,MAAM,QAAQ;AACzC,UAAI,CAAC,OAAO;AACV,eAAO,KAAK,4BAA4B,QAAQ,mBAAmB;AACnE;AAAA,MACF;AAGA,YAAM,iBAAiB,MAAM,MAAM,SAAY,QAAQ,MAAM,CAAC,IAAI;AAClE,YAAM,mBAAmB,mBAAmB;AAE5C,UAAI,kBAAkB;AACpB,eAAO,KAAK,QAAQ;AACpB;AAAA,MACF;AAGA,YAAM,WAAW,MAAM,MAAM,UAAa,mBAAmB;AAE7D,UAAI;AACF,YAAI;AAEJ,YAAI,UAAU;AAEZ,gBAAM,YAAY,GAAG,cAAc,GAAG,QAAQ;AAC9C,2BAAiB,MAAM,sBAAsB,SAAS;AAAA,QACxD,OAAO;AAEL,gBAAM,oBAAoBC,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AACrE,cAAI;AACF,6BAAiB,MAAMC,IAAG,SAAS,iBAAiB;AAAA,UACtD,QAAQ;AACN,mBAAO,KAAK,4BAA4B,QAAQ,EAAE;AAClD;AAAA,UACF;AAAA,QACF;AAGA,cAAM,GAAG;AAAA,UACP,IAAIC,kBAAiB;AAAA,YACnB,QAAQ;AAAA,YACR,KAAK,SAAS,QAAQ,OAAO,EAAE;AAAA,YAC/B,MAAM;AAAA,YACN,aAAa,eAAe,QAAQ;AAAA,UACtC,CAAC;AAAA,QACH;AACA,oBAAY,KAAK,GAAG,SAAS,GAAG,QAAQ,EAAE;AAG1C,YAAI,CAAC,YAAY,YAAY,KAAK,GAAG;AACnC,qBAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,kBAAM,YAAYF,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AAC9D,gBAAI;AACF,oBAAM,aAAa,MAAMC,IAAG,SAAS,SAAS;AAC9C,oBAAM,GAAG;AAAA,gBACP,IAAIC,kBAAiB;AAAA,kBACnB,QAAQ;AAAA,kBACR,KAAK,UAAU,QAAQ,OAAO,EAAE;AAAA,kBAChC,MAAM;AAAA,kBACN,aAAa,eAAe,SAAS;AAAA,gBACvC,CAAC;AAAA,cACH;AACA,0BAAY,KAAK,GAAG,SAAS,GAAG,SAAS,EAAE;AAAA,YAC7C,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AAEA,cAAM,IAAI;AAGV,YAAI,CAAC,UAAU;AACb,gBAAM,oBAAoBF,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAGrE,qBAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,kBAAM,YAAYA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,SAAS;AAC9D,gBAAI;AAAE,oBAAMC,IAAG,OAAO,SAAS;AAAA,YAAE,QAAQ;AAAA,YAAe;AAAA,UAC1D;AAGA,cAAI;AAAE,kBAAMA,IAAG,OAAO,iBAAiB;AAAA,UAAE,QAAQ;AAAA,UAAe;AAAA,QAClE;AAEA,eAAO,KAAK,QAAQ;AAAA,MACtB,SAAS,OAAO;AACd,gBAAQ,MAAM,kBAAkB,QAAQ,KAAK,KAAK;AAClD,eAAO,KAAK,mBAAmB,QAAQ,EAAE;AAAA,MAC3C;AAAA,IACF;AAEA,UAAM,SAAS,IAAI;AAGnB,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,qBAAqB,WAAW;AAAA,IACxC;AAEA,WAAOH,cAAa,KAAK;AAAA,MACvB,SAAS;AAAA,MACT;AAAA,MACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACvC,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,mBAAmB,KAAK;AACtC,WAAOA,cAAa,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9E;AACF;AAEA,eAAsB,gBAAgB,SAAsB;AAC1D,QAAM,YAAY,QAAQ,IAAI,0BAA0B,QAAQ,UAAU,EAAE;AAE5E,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,QAAQ,KAAK;AAEzC,QAAI,CAAC,aAAa,CAAC,MAAM,QAAQ,SAAS,KAAK,UAAU,WAAW,GAAG;AACrE,aAAOA,cAAa,KAAK,EAAE,OAAO,yBAAyB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/E;AAEA,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,UAAU,WAAW,IAAI;AAC/B,UAAM,YAAsB,CAAC;AAC7B,UAAM,SAAmB,CAAC;AAC1B,UAAM,cAAwB,CAAC;AAE/B,aAAS,YAAY,WAAW;AAE9B,UAAI,CAAC,SAAS,WAAW,GAAG,GAAG;AAC7B,mBAAW,IAAI,QAAQ;AAAA,MACzB;AAEA,UAAI;AACF,YAAI;AACJ,cAAM,QAAQ,aAAa,MAAM,QAAQ;AACzC,cAAM,mBAAmB,OAAO;AAChC,cAAM,iBAAiB,qBAAqB,SAAY,QAAQ,gBAAgB,IAAI;AAGpF,cAAM,YAAY,mBAAmB;AACrC,cAAM,WAAW,qBAAqB,UAAa,CAAC;AAEpD,cAAM,eAAeE,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAEhE,YAAI;AACF,mBAAS,MAAMC,IAAG,SAAS,YAAY;AAAA,QACzC,QAAQ;AACN,cAAI,WAAW;AAEb,qBAAS,MAAM,gBAAgB,QAAQ;AACvC,kBAAM,MAAMD,MAAK,QAAQ,YAAY;AACrC,kBAAMC,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,kBAAMA,IAAG,UAAU,cAAc,MAAM;AAAA,UACzC,WAAW,YAAY,gBAAgB;AAErC,kBAAM,YAAY,GAAG,cAAc,GAAG,QAAQ;AAC9C,qBAAS,MAAM,sBAAsB,SAAS;AAC9C,kBAAM,MAAMD,MAAK,QAAQ,YAAY;AACrC,kBAAMC,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,kBAAMA,IAAG,UAAU,cAAc,MAAM;AAAA,UACzC,OAAO;AACL,kBAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AAAA,UAC/C;AAAA,QACF;AAEA,cAAM,eAAe,MAAM,aAAa,QAAQ,QAAQ;AAGxD,YAAI,WAAW;AAEb,uBAAa,IAAI;AACjB,gBAAM,YAAY,QAAQ;AAG1B,qBAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,wBAAY,KAAK,GAAG,SAAS,GAAG,SAAS,EAAE;AAAA,UAC7C;AAEA,gBAAM,sBAAsB,QAAQ;AAEpC,cAAI;AAAE,kBAAMA,IAAG,OAAO,YAAY;AAAA,UAAE,QAAQ;AAAA,UAAe;AAAA,QAC7D,WAAW,UAAU;AAAA,QAGrB;AAEA,aAAK,QAAQ,IAAI;AACjB,kBAAU,KAAK,QAAQ;AAAA,MACzB,SAAS,OAAO;AACd,gBAAQ,MAAM,uBAAuB,QAAQ,KAAK,KAAK;AACvD,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,SAAS,IAAI;AAGnB,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,qBAAqB,WAAW;AAAA,IACxC;AAEA,WAAOH,cAAa,KAAK;AAAA,MACvB,SAAS;AAAA,MACT;AAAA,MACA,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,IACvC,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,wBAAwB,KAAK;AAC3C,WAAOA,cAAa,KAAK,EAAE,OAAO,6BAA6B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnF;AACF;AAEA,eAAsB,yBAAyB;AAC7C,QAAM,YAAY,QAAQ,IAAI,0BAA0B,QAAQ,UAAU,EAAE;AAC5E,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,MAAM,YAAY;AACtB,YAAM,YAAY,CAAC,SAAiB;AAClC,mBAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM,CAAC;AAAA,MACxE;AAEA,UAAI;AACF,cAAM,OAAO,MAAM,SAAS;AAC5B,cAAM,UAAU,WAAW,IAAI;AAC/B,cAAM,YAAsB,CAAC;AAC7B,cAAM,SAAmB,CAAC;AAC1B,cAAM,iBAA2B,CAAC;AAClC,cAAM,cAAwB,CAAC;AAG/B,YAAI,mBAAmB;AAGvB,cAAM,kBAA+E,CAAC;AAEtF,mBAAW,CAAC,KAAK,KAAK,KAAK,eAAe,IAAI,GAAG;AAC/C,gBAAM,WAAWE,MAAK,SAAS,GAAG;AAClC,cAAI,CAAC,YAAY,QAAQ,EAAG;AAG5B,cAAI,CAAC,YAAY,KAAK,GAAG;AACvB,4BAAgB,KAAK,EAAE,KAAK,MAAM,CAAC;AAAA,UACrC,OAAO;AACL;AAAA,UACF;AAAA,QACF;AAEA,cAAM,QAAQ,gBAAgB;AAC9B,kBAAU,EAAE,MAAM,SAAS,MAAM,CAAC;AAElC,iBAAS,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;AAC/C,gBAAM,EAAE,KAAK,MAAM,IAAI,gBAAgB,CAAC;AACxC,gBAAM,WAAWA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,GAAG;AACvD,gBAAM,mBAAmB,MAAM;AAC/B,gBAAM,iBAAiB,qBAAqB,SAAY,QAAQ,gBAAgB,IAAI;AAGpF,gBAAM,YAAY,mBAAmB;AACrC,gBAAM,WAAW,qBAAqB,UAAa,CAAC;AAEpD,oBAAU;AAAA,YACR,MAAM;AAAA,YACN,SAAS,IAAI;AAAA,YACb;AAAA,YACA,SAAS,KAAK,OAAQ,IAAI,KAAK,QAAS,GAAG;AAAA,YAC3C,aAAa,IAAI,MAAM,CAAC;AAAA;AAAA,UAC1B,CAAC;AAED,cAAI;AACF,gBAAI;AAGJ,gBAAI,WAAW;AACb,uBAAS,MAAM,gBAAgB,GAAG;AAClC,oBAAM,MAAMA,MAAK,QAAQ,QAAQ;AACjC,oBAAMC,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,oBAAMA,IAAG,UAAU,UAAU,MAAM;AAAA,YACrC,WAAW,YAAY,gBAAgB;AACrC,oBAAM,YAAY,GAAG,cAAc,GAAG,GAAG;AACzC,uBAAS,MAAM,sBAAsB,SAAS;AAC9C,oBAAM,MAAMD,MAAK,QAAQ,QAAQ;AACjC,oBAAMC,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,oBAAMA,IAAG,UAAU,UAAU,MAAM;AAAA,YACrC,OAAO;AACL,uBAAS,MAAMA,IAAG,SAAS,QAAQ;AAAA,YACrC;AAEA,kBAAM,MAAMD,MAAK,QAAQ,GAAG,EAAE,YAAY;AAC1C,kBAAM,QAAQ,QAAQ;AAEtB,gBAAI,OAAO;AACT,oBAAM,WAAWA,MAAK,QAAQ,IAAI,MAAM,CAAC,CAAC;AAC1C,oBAAM,aAAaA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,UAAU,aAAa,MAAM,KAAK,QAAQ;AAChG,oBAAMC,IAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAE9C,oBAAM,WAAWD,MAAK,SAAS,GAAG;AAClC,oBAAM,WAAWA,MAAK,KAAK,YAAY,QAAQ;AAC/C,oBAAMC,IAAG,UAAU,UAAU,MAAM;AAEnC,mBAAK,GAAG,IAAI;AAAA,gBACV,GAAG;AAAA,gBACH,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,gBAChB,GAAG;AAAA,gBACH,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA;AAAA,cAClB;AAGA,kBAAI,UAAU;AACZ,uBAAQ,KAAK,GAAG,EAAmC;AAAA,cACrD;AAAA,YACF,OAAO;AACL,oBAAM,iBAAiB,MAAM,aAAa,QAAQ,GAAG;AACrD,mBAAK,GAAG,IAAI;AAAA,gBACV,GAAG;AAAA,gBACH,GAAI,YAAY,EAAE,GAAG,iBAAiB,IAAI,CAAC;AAAA,cAC7C;AAAA,YAEF;AAGA,gBAAI,WAAW;AACb,oBAAM,YAAY,GAAG;AAGrB,yBAAW,aAAa,qBAAqB,GAAG,GAAG;AACjD,4BAAY,KAAK,GAAG,SAAS,GAAG,SAAS,EAAE;AAAA,cAC7C;AAEA,oBAAM,sBAAsB,GAAG;AAE/B,kBAAI;AAAE,sBAAMA,IAAG,OAAO,QAAQ;AAAA,cAAE,QAAQ;AAAA,cAAe;AAAA,YACzD;AAGA,sBAAU,KAAK,IAAI,MAAM,CAAC,CAAC;AAAA,UAC7B,SAAS,OAAO;AACd,oBAAQ,MAAM,qBAAqB,GAAG,KAAK,KAAK;AAChD,mBAAO,KAAK,IAAI,MAAM,CAAC,CAAC;AAAA,UAC1B;AAAA,QACF;AAEA,kBAAU,EAAE,MAAM,WAAW,SAAS,kCAAkC,CAAC;AAGzE,cAAM,eAAe,oBAAI,IAAY;AACrC,mBAAW,CAAC,UAAU,KAAK,KAAK,eAAe,IAAI,GAAG;AAEpD,cAAI,MAAM,MAAM,QAAW;AACzB,uBAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,2BAAa,IAAI,SAAS;AAAA,YAC5B;AAAA,UACF;AAAA,QACF;AAEA,uBAAe,YAAY,KAAa,eAAuB,IAAmB;AAChF,cAAI;AACF,kBAAM,UAAU,MAAMA,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAE7D,uBAAW,WAAW,SAAS;AAC7B,kBAAI,QAAQ,KAAK,WAAW,GAAG,EAAG;AAElC,oBAAM,gBAAgBD,MAAK,KAAK,KAAK,QAAQ,IAAI;AACjD,oBAAM,UAAU,eAAe,GAAG,YAAY,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAE3E,kBAAI,QAAQ,YAAY,GAAG;AACzB,sBAAM,YAAY,eAAe,OAAO;AAAA,cAC1C,WAAW,YAAY,QAAQ,IAAI,GAAG;AACpC,sBAAM,aAAa,WAAW,OAAO;AACrC,oBAAI,CAAC,aAAa,IAAI,UAAU,GAAG;AACjC,sBAAI;AACF,0BAAMC,IAAG,OAAO,aAAa;AAC7B,mCAAe,KAAK,UAAU;AAAA,kBAChC,SAAS,KAAK;AACZ,4BAAQ,MAAM,2BAA2B,UAAU,KAAK,GAAG;AAAA,kBAC7D;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAEA,cAAM,YAAYD,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAC7D,YAAI;AACF,gBAAM,YAAY,SAAS;AAAA,QAC7B,QAAQ;AAAA,QAER;AAEA,uBAAe,gBAAgB,KAA+B;AAC5D,cAAI;AACF,kBAAM,UAAU,MAAMC,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC7D,gBAAI,UAAU;AAEd,uBAAW,WAAW,SAAS;AAC7B,kBAAI,QAAQ,YAAY,GAAG;AACzB,sBAAM,cAAc,MAAM,gBAAgBD,MAAK,KAAK,KAAK,QAAQ,IAAI,CAAC;AACtE,oBAAI,CAAC,YAAa,WAAU;AAAA,cAC9B,OAAO;AACL,0BAAU;AAAA,cACZ;AAAA,YACF;AAEA,gBAAI,WAAW,QAAQ,WAAW;AAChC,oBAAMC,IAAG,MAAM,GAAG;AAAA,YACpB;AAEA,mBAAO;AAAA,UACT,QAAQ;AACN,mBAAO;AAAA,UACT;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,gBAAgB,SAAS;AAAA,QACjC,QAAQ;AAAA,QAER;AAEA,cAAM,SAAS,IAAI;AAGnB,YAAI,YAAY,SAAS,GAAG;AAC1B,gBAAM,qBAAqB,WAAW;AAAA,QACxC;AAEA,kBAAU;AAAA,UACR,MAAM;AAAA,UACN,WAAW,UAAU;AAAA,UACrB;AAAA,UACA,gBAAgB,eAAe;AAAA,UAC/B,QAAQ,OAAO;AAAA,QACjB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,0BAA0B,KAAK;AAC7C,kBAAU,EAAE,MAAM,SAAS,SAAS,2BAA2B,CAAC;AAAA,MAClE,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,QAAQ;AAAA,IAC1B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;;;ACngBA,SAAsB,gBAAAE,qBAAoB;AAC1C,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,SAAS,UAAAC,eAAc;AAUvB,eAAsB,mBAAmB;AACvC,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,MAAM,YAAY;AACtB,YAAM,YAAY,CAAC,SAAiB;AAClC,mBAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM,CAAC;AAAA,MACxE;AAEA,UAAI;AACF,cAAM,OAAO,MAAM,SAAS;AAC5B,cAAM,gBAAgB,OAAO,KAAK,IAAI,EAAE,OAAO,OAAK,CAAC,EAAE,WAAW,GAAG,CAAC,EAAE;AACxE,cAAM,eAAe,IAAI,IAAI,OAAO,KAAK,IAAI,CAAC;AAC9C,cAAM,QAAkB,CAAC;AACzB,cAAM,UAA+C,CAAC;AACtD,cAAM,SAAmB,CAAC;AAC1B,cAAM,gBAA0B,CAAC;AAGjC,cAAM,WAA8D,CAAC;AAErE,uBAAe,QAAQ,KAAa,eAAuB,IAAmB;AAC5E,cAAI;AACF,kBAAM,UAAU,MAAMC,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAE7D,uBAAW,SAAS,SAAS;AAC3B,kBAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAEhC,oBAAM,WAAWC,MAAK,KAAK,KAAK,MAAM,IAAI;AAC1C,oBAAM,UAAU,eAAe,GAAG,YAAY,IAAI,MAAM,IAAI,KAAK,MAAM;AAGvE,kBAAI,YAAY,YAAY,QAAQ,WAAW,SAAS,EAAG;AAE3D,kBAAI,MAAM,YAAY,GAAG;AACvB,sBAAM,QAAQ,UAAU,OAAO;AAAA,cACjC,WAAW,YAAY,MAAM,IAAI,GAAG;AAClC,yBAAS,KAAK,EAAE,cAAc,SAAS,SAAS,CAAC;AAAA,cACnD;AAAA,YACF;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAEA,cAAM,YAAYA,MAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AACnD,cAAM,QAAQ,SAAS;AAEvB,cAAM,QAAQ,SAAS;AACvB,kBAAU,EAAE,MAAM,SAAS,MAAM,CAAC;AAElC,iBAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,cAAI,EAAE,cAAc,SAAS,IAAI,SAAS,CAAC;AAC3C,cAAI,WAAW,MAAM;AAErB,oBAAU;AAAA,YACR,MAAM;AAAA,YACN,SAAS,IAAI;AAAA,YACb;AAAA,YACA,SAAS,KAAK,OAAQ,IAAI,KAAK,QAAS,GAAG;AAAA,YAC3C,aAAa;AAAA,UACf,CAAC;AAGD,cAAI,aAAa,IAAI,QAAQ,GAAG;AAE9B;AAAA,UACF;AAGA,cAAI,KAAK,QAAQ,GAAG;AAElB,kBAAM,MAAMA,MAAK,QAAQ,YAAY;AACrC,kBAAM,WAAW,aAAa,MAAM,GAAG,CAAC,IAAI,MAAM;AAClD,gBAAI,UAAU;AACd,gBAAI,SAAS,IAAI,QAAQ,IAAI,OAAO,GAAG,GAAG;AAE1C,mBAAO,KAAK,MAAM,GAAG;AACnB;AACA,uBAAS,IAAI,QAAQ,IAAI,OAAO,GAAG,GAAG;AAAA,YACxC;AAGA,kBAAM,kBAAkB,GAAG,QAAQ,IAAI,OAAO,GAAG,GAAG;AACpD,kBAAM,cAAcA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,eAAe;AAEtE,gBAAI;AACF,oBAAMD,IAAG,OAAO,UAAU,WAAW;AACrC,sBAAQ,KAAK,EAAE,MAAM,cAAc,IAAI,gBAAgB,CAAC;AACxD,6BAAe;AACf,yBAAW;AACX,yBAAW;AAAA,YACb,SAAS,KAAK;AACZ,sBAAQ,MAAM,oBAAoB,YAAY,KAAK,GAAG;AACtD,qBAAO,KAAK,oBAAoB,YAAY,EAAE;AAC9C;AAAA,YACF;AAAA,UACF;AAEA,cAAI;AACF,kBAAM,UAAU,YAAY,YAAY;AAExC,gBAAI,SAAS;AAEX,oBAAM,MAAMC,MAAK,QAAQ,YAAY,EAAE,YAAY;AAEnD,kBAAI,QAAQ,QAAQ;AAElB,qBAAK,QAAQ,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG;AAAA,cAC9C,OAAO;AACL,oBAAI;AACF,wBAAM,SAAS,MAAMD,IAAG,SAAS,QAAQ;AACzC,wBAAM,WAAW,MAAME,OAAM,MAAM,EAAE,SAAS;AAG9C,wBAAM,EAAE,MAAM,KAAK,IAAI,MAAMA,OAAM,MAAM,EACtC,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,CAAC,EAChC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,wBAAM,WAAWC,QAAO,IAAI,kBAAkB,IAAI,GAAG,KAAK,OAAO,KAAK,QAAQ,GAAG,CAAC;AAElF,uBAAK,QAAQ,IAAI;AAAA,oBACf,GAAG,EAAE,GAAG,SAAS,SAAS,GAAG,GAAG,SAAS,UAAU,EAAE;AAAA,oBACrD,GAAG;AAAA,kBACL;AAAA,gBACF,QAAQ;AAEN,uBAAK,QAAQ,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,EAAE;AAAA,gBACvC;AAAA,cACF;AAAA,YACF,OAAO;AAEL,mBAAK,QAAQ,IAAI,CAAC;AAAA,YACpB;AAEA,yBAAa,IAAI,QAAQ;AACzB,kBAAM,KAAK,QAAQ;AAAA,UACrB,SAAS,OAAO;AACd,oBAAQ,MAAM,qBAAqB,YAAY,KAAK,KAAK;AACzD,mBAAO,KAAK,YAAY;AAAA,UAC1B;AAAA,QACF;AAGA,kBAAU,EAAE,MAAM,WAAW,SAAS,sCAAsC,CAAC;AAG7E,cAAM,qBAAqB,oBAAI,IAAY;AAC3C,cAAM,cAAc,eAAe,IAAI;AACvC,mBAAW,CAAC,UAAU,KAAK,KAAK,aAAa;AAE3C,cAAI,MAAM,MAAM,UAAa,YAAY,KAAK,GAAG;AAC/C,uBAAW,aAAa,qBAAqB,QAAQ,GAAG;AACtD,iCAAmB,IAAI,SAAS;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAGA,uBAAe,YAAY,KAAa,eAAuB,IAAmB;AAChF,cAAI;AACF,kBAAM,UAAU,MAAMH,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAE7D,uBAAW,SAAS,SAAS;AAC3B,kBAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAEhC,oBAAM,WAAWC,MAAK,KAAK,KAAK,MAAM,IAAI;AAC1C,oBAAM,UAAU,eAAe,GAAG,YAAY,IAAI,MAAM,IAAI,KAAK,MAAM;AAEvE,kBAAI,MAAM,YAAY,GAAG;AACvB,sBAAM,YAAY,UAAU,OAAO;AAAA,cACrC,WAAW,YAAY,MAAM,IAAI,GAAG;AAClC,sBAAM,aAAa,WAAW,OAAO;AACrC,oBAAI,CAAC,mBAAmB,IAAI,UAAU,GAAG;AACvC,gCAAc,KAAK,UAAU;AAAA,gBAC/B;AAAA,cACF;AAAA,YACF;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAEA,cAAM,YAAYA,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAC7D,YAAI;AACF,gBAAM,YAAY,SAAS;AAAA,QAC7B,QAAQ;AAAA,QAER;AAEA,cAAM,SAAS,IAAI;AAEnB,kBAAU;AAAA,UACR,MAAM;AAAA,UACN;AAAA,UACA,OAAO,MAAM;AAAA,UACb,SAAS,QAAQ;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,cAAc;AAAA,UACd,eAAe,cAAc,SAAS,IAAI,gBAAgB;AAAA,QAC5D,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,gBAAgB,KAAK;AACnC,kBAAU,EAAE,MAAM,SAAS,SAAS,cAAc,CAAC;AAAA,MACrD,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,QAAQ;AAAA,IAC1B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;AAKA,eAAsB,oBAAoB,SAAsB;AAC9D,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,QAAQ,KAAK;AAErC,QAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AACzD,aAAOG,cAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1E;AAEA,UAAM,UAAoB,CAAC;AAC3B,UAAM,SAAmB,CAAC;AAE1B,eAAW,cAAc,OAAO;AAE9B,UAAI,CAAC,WAAW,WAAW,UAAU,GAAG;AACtC,eAAO,KAAK,iBAAiB,UAAU,EAAE;AACzC;AAAA,MACF;AAEA,YAAM,WAAWH,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,UAAU;AAE9D,UAAI;AACF,cAAMD,IAAG,OAAO,QAAQ;AACxB,gBAAQ,KAAK,UAAU;AAAA,MACzB,SAAS,KAAK;AACZ,gBAAQ,MAAM,oBAAoB,UAAU,KAAK,GAAG;AACpD,eAAO,KAAK,UAAU;AAAA,MACxB;AAAA,IACF;AAGA,UAAM,YAAYC,MAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAE7D,mBAAe,gBAAgB,KAA+B;AAC5D,UAAI;AACF,cAAM,UAAU,MAAMD,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC7D,YAAI,UAAU;AAEd,mBAAW,SAAS,SAAS;AAC3B,cAAI,MAAM,YAAY,GAAG;AACvB,kBAAM,cAAc,MAAM,gBAAgBC,MAAK,KAAK,KAAK,MAAM,IAAI,CAAC;AACpE,gBAAI,CAAC,YAAa,WAAU;AAAA,UAC9B,OAAO;AACL,sBAAU;AAAA,UACZ;AAAA,QACF;AAEA,YAAI,WAAW,QAAQ,WAAW;AAChC,gBAAMD,IAAG,MAAM,GAAG;AAAA,QACpB;AAEA,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI;AACF,YAAM,gBAAgB,SAAS;AAAA,IACjC,QAAQ;AAAA,IAER;AAEA,WAAOI,cAAa,KAAK;AAAA,MACvB,SAAS;AAAA,MACT,SAAS,QAAQ;AAAA,MACjB,QAAQ,OAAO;AAAA,IACjB,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,6BAA6B,KAAK;AAChD,WAAOA,cAAa,KAAK,EAAE,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACxF;AACF;;;ACpTA,OAAOC,YAAW;AAClB,SAAS,UAAAC,eAAc;AAavB,SAAS,cAAc,KAA6C;AAClE,QAAM,SAAS,IAAI,IAAI,GAAG;AAE1B,QAAM,OAAO,GAAG,OAAO,QAAQ,KAAK,OAAO,IAAI;AAE/C,QAAMC,QAAO,OAAO;AACpB,SAAO,EAAE,MAAM,MAAAA,MAAK;AACtB;AAKA,eAAe,mBAAmB,KAAoD;AACpF,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM,EAAE;AAAA,EACvD;AAEA,QAAM,SAAS,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAEvD,QAAM,WAAW,MAAMC,OAAM,MAAM,EAAE,SAAS;AAG9C,QAAM,EAAE,MAAM,KAAK,IAAI,MAAMA,OAAM,MAAM,EACtC,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,CAAC,EAChC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,QAAM,WAAWC,QAAO,IAAI,kBAAkB,IAAI,GAAG,KAAK,OAAO,KAAK,QAAQ,GAAG,CAAC;AAElF,SAAO;AAAA,IACL,GAAG,EAAE,GAAG,SAAS,SAAS,GAAG,GAAG,SAAS,UAAU,EAAE;AAAA,IACrD,GAAG;AAAA,EACL;AACF;AAKA,eAAsB,iBAAiB,SAAsB;AAC3D,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,MAAM,YAAY;AACtB,YAAM,YAAY,CAAC,SAAiB;AAClC,mBAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM,CAAC;AAAA,MACxE;AAEA,UAAI;AACF,cAAM,EAAE,KAAK,IAAI,MAAM,QAAQ,KAAK;AAEpC,YAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,GAAG;AACtD,oBAAU,EAAE,MAAM,SAAS,SAAS,mBAAmB,CAAC;AACxD,qBAAW,MAAM;AACjB;AAAA,QACF;AAEA,cAAM,OAAO,MAAM,SAAS;AAC5B,cAAM,QAAkB,CAAC;AACzB,cAAM,UAAoB,CAAC;AAC3B,cAAM,SAAmB,CAAC;AAE1B,cAAM,QAAQ,KAAK;AACnB,kBAAU,EAAE,MAAM,SAAS,MAAM,CAAC;AAElC,iBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,gBAAM,MAAM,KAAK,CAAC,EAAE,KAAK;AACzB,cAAI,CAAC,IAAK;AAEV,oBAAU;AAAA,YACR,MAAM;AAAA,YACN,SAAS,IAAI;AAAA,YACb;AAAA,YACA,SAAS,KAAK,OAAQ,IAAI,KAAK,QAAS,GAAG;AAAA,YAC3C,aAAa;AAAA,UACf,CAAC;AAED,cAAI;AAEF,kBAAM,EAAE,MAAM,MAAAF,MAAK,IAAI,cAAc,GAAG;AAGxC,kBAAM,gBAAgB,aAAa,MAAMA,KAAI;AAC7C,gBAAI,eAAe;AACjB,sBAAQ,KAAKA,KAAI;AACjB;AAAA,YACF;AAGA,kBAAM,WAAW,iBAAiB,MAAM,IAAI;AAG5C,kBAAM,YAAY,MAAM,mBAAmB,GAAG;AAI9C,yBAAa,MAAMA,OAAM;AAAA,cACvB,GAAG,UAAU;AAAA,cACb,GAAG,UAAU;AAAA,cACb,GAAG;AAAA,YACL,CAAC;AAED,kBAAM,KAAKA,KAAI;AAAA,UACjB,SAAS,OAAO;AACd,oBAAQ,MAAM,oBAAoB,GAAG,KAAK,KAAK;AAC/C,mBAAO,KAAK,GAAG;AAAA,UACjB;AAAA,QACF;AAEA,cAAM,SAAS,IAAI;AAEnB,kBAAU;AAAA,UACR,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,SAAS,QAAQ;AAAA,UACjB,QAAQ,OAAO;AAAA,QACjB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,kBAAkB,KAAK;AACrC,kBAAU,EAAE,MAAM,SAAS,SAAS,gBAAgB,CAAC;AAAA,MACvD,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,QAAQ;AAAA,IAC1B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;AAKA,eAAsB,gBAAgB;AACpC,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,OAAO,KAAK,SAAS,CAAC;AAE5B,WAAO,SAAS,KAAK,EAAE,KAAK,CAAC;AAAA,EAC/B,SAAS,OAAO;AACd,YAAQ,MAAM,uBAAuB,KAAK;AAC1C,WAAO,SAAS,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;AAKA,eAAsB,iBAAiB,SAAsB;AAC3D,MAAI;AACF,UAAM,EAAE,KAAK,IAAI,MAAM,QAAQ,KAAK;AAEpC,QAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,aAAO,SAAS,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACtE;AAEA,UAAM,OAAO,MAAM,SAAS;AAG5B,SAAK,QAAQ,KAAK,IAAI,SAAO,IAAI,QAAQ,OAAO,EAAE,CAAC;AAEnD,UAAM,SAAS,IAAI;AAEnB,WAAO,SAAS,KAAK,EAAE,SAAS,MAAM,MAAM,KAAK,MAAM,CAAC;AAAA,EAC1D,SAAS,OAAO;AACd,YAAQ,MAAM,0BAA0B,KAAK;AAC7C,WAAO,SAAS,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1E;AACF;;;ATzKA,eAAsB,IAAI,SAAsB;AAC9C,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,WAAOG,cAAa,KAAK,EAAE,OAAO,8BAA8B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpF;AAEA,QAAM,WAAW,QAAQ,QAAQ;AACjC,QAAM,QAAQ,SAAS,QAAQ,qBAAqB,EAAE;AAGtD,MAAI,UAAU,gBAAgB;AAC5B,WAAO,kBAAkB;AAAA,EAC3B;AAGA,MAAI,UAAU,UAAU,MAAM,WAAW,MAAM,GAAG;AAChD,WAAO,WAAW,OAAO;AAAA,EAC3B;AAGA,MAAI,UAAU,gBAAgB;AAC5B,WAAO,kBAAkB;AAAA,EAC3B;AAGA,MAAI,UAAU,iBAAiB;AAC7B,WAAO,mBAAmB,OAAO;AAAA,EACnC;AAGA,MAAI,UAAU,UAAU;AACtB,WAAO,aAAa,OAAO;AAAA,EAC7B;AAGA,MAAI,UAAU,QAAQ;AACpB,WAAO,cAAc;AAAA,EACvB;AAEA,SAAOA,cAAa,KAAK,EAAE,OAAO,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAClE;AAKA,eAAsB,KAAK,SAAsB;AAC/C,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,WAAOA,cAAa,KAAK,EAAE,OAAO,8BAA8B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpF;AAEA,QAAM,WAAW,QAAQ,QAAQ;AACjC,QAAM,QAAQ,SAAS,QAAQ,qBAAqB,EAAE;AAGtD,MAAI,UAAU,UAAU;AACtB,WAAO,aAAa,OAAO;AAAA,EAC7B;AAGA,MAAI,UAAU,UAAU;AACtB,WAAO,aAAa,OAAO;AAAA,EAC7B;AAGA,MAAI,UAAU,QAAQ;AACpB,WAAO,WAAW,OAAO;AAAA,EAC3B;AAGA,MAAI,UAAU,aAAa;AACzB,WAAO,gBAAgB,OAAO;AAAA,EAChC;AAGA,MAAI,UAAU,eAAe;AAC3B,WAAO,uBAAuB;AAAA,EAChC;AAGA,MAAI,UAAU,iBAAiB;AAC7B,WAAO,mBAAmB,OAAO;AAAA,EACnC;AAGA,MAAI,UAAU,UAAU;AACtB,WAAO,aAAa,OAAO;AAAA,EAC7B;AAGA,MAAI,UAAU,QAAQ;AACpB,WAAO,iBAAiB,OAAO;AAAA,EACjC;AAGA,MAAI,UAAU,QAAQ;AACpB,WAAO,iBAAiB;AAAA,EAC1B;AAGA,MAAI,UAAU,kBAAkB;AAC9B,WAAO,oBAAoB,OAAO;AAAA,EACpC;AAGA,MAAI,UAAU,UAAU;AACtB,WAAO,iBAAiB,OAAO;AAAA,EACjC;AAGA,MAAI,UAAU,QAAQ;AACpB,WAAO,iBAAiB,OAAO;AAAA,EACjC;AAEA,SAAOA,cAAa,KAAK,EAAE,OAAO,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAClE;AAKA,eAAsB,OAAO,SAAsB;AACjD,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,WAAOA,cAAa,KAAK,EAAE,OAAO,8BAA8B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpF;AAEA,SAAO,aAAa,OAAO;AAC7B;","names":["NextResponse","fs","path","path","fs","path","fs","path","path","fs","path","fs","NextResponse","fs","path","sharp","NextResponse","path","fs","sharp","NextResponse","fs","path","S3Client","PutObjectCommand","NextResponse","S3Client","path","fs","PutObjectCommand","NextResponse","fs","path","sharp","encode","fs","path","sharp","encode","NextResponse","sharp","encode","path","sharp","encode","NextResponse"]}