next-file-manager 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/server/config.ts","../../src/server/database/mongoose/schema/drive.ts","../../src/server/database/mongoose/schema/storage/account.ts","../../src/server/utils.ts","../../src/server/zod/schemas.ts","../../src/server/security/cryptoUtils.ts","../../src/server/providers/local.ts","../../src/server/providers/google.ts","../../src/server/controllers/drive.ts"],"sourcesContent":["// ** Server Entry Point\nimport type { NextApiRequest, NextApiResponse } from 'next';\nimport formidable from 'formidable';\nimport path from 'path';\nimport fs from 'fs';\nimport { z } from 'zod';\n\nimport { getDriveConfig, getDriveInformation, driveConfiguration } from '@/server/config';\nimport Drive from '@/server/database/mongoose/schema/drive';\nimport StorageAccount from '@/server/database/mongoose/schema/storage/account';\nimport { validateMimeType } from '@/server/utils';\nimport * as schemas from '@/server/zod/schemas';\nimport { getSafeErrorMessage, sanitizeContentDispositionFilename } from '@/server/security/cryptoUtils';\nimport type { TDatabaseDrive } from '@/types/server';\n\n// ** Providers\nimport { LocalStorageProvider } from '@/server/providers/local';\nimport { GoogleDriveProvider } from '@/server/providers/google';\nimport type { TStorageProvider } from '@/types/server/storage';\n\n// ** Helper to get Provider\nconst getProvider = async (req: NextApiRequest, owner: Record<string, unknown> | null): Promise<{ provider: TStorageProvider, accountId?: string }> => {\n // Check header for account ID\n const accountId = req.headers['x-drive-account'] as string;\n\n if (!accountId || accountId === 'LOCAL') {\n return { provider: LocalStorageProvider };\n }\n\n // Validate account belongs to owner\n const account = await StorageAccount.findOne({ _id: accountId, owner });\n if (!account) {\n throw new Error('Invalid Storage Account');\n }\n\n if (account.metadata.provider === 'GOOGLE') return { provider: GoogleDriveProvider, accountId: account._id.toString() };\n\n // Fallback\n return { provider: LocalStorageProvider };\n};\n\n// ** Main API handler for all drive operations\nexport const driveAPIHandler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {\n const action = req.query.action as string;\n\n // ** Ensure Config\n try {\n getDriveConfig();\n } catch (error) {\n console.error('[next-file-manager] Configuration error:', error);\n res.status(500).json({ status: 500, message: 'Failed to initialize drive configuration' });\n return;\n }\n\n if (!action) {\n res.status(400).json({ status: 400, message: 'Missing action query parameter' });\n return;\n }\n\n try {\n const config = getDriveConfig();\n const information = await getDriveInformation(req);\n const { key: owner } = information;\n const STORAGE_PATH = config.storage.path;\n\n // ** OAuth & Account Actions (No Provider Resolution Needed)\n if (['getAuthUrl', 'callback', 'listAccounts', 'removeAccount'].includes(action)) {\n switch (action) {\n case 'getAuthUrl': {\n const { provider } = req.query;\n if (provider === 'GOOGLE') {\n const { clientId, clientSecret, redirectUri } = config.storage?.google || {};\n if (!clientId || !clientSecret) return res.status(500).json({ status: 500, message: 'Google not configured' });\n\n const { google } = require('googleapis');\n const oAuth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);\n // Generate state to identify user\n // For security, should sign state. Simple base64 for now.\n const state = Buffer.from(JSON.stringify({ owner })).toString('base64');\n const url = oAuth2Client.generateAuthUrl({\n access_type: 'offline',\n scope: ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/userinfo.email'],\n state,\n prompt: 'consent' // force refresh token\n });\n return res.status(200).json({ status: 200, message: 'Auth URL generated', data: { url } });\n }\n return res.status(400).json({ status: 400, message: 'Unknown provider' });\n }\n case 'callback': {\n const { code, state } = req.query;\n if (!code) return res.status(400).json({ status: 400, message: 'Missing code' });\n\n // Verify state if possible, though 'owner' is derived from session/request usually.\n // Assuming 'owner' from getDriveInformation is the source of truth for current session.\n\n const { clientId, clientSecret, redirectUri } = config.storage?.google || {};\n const { google } = require('googleapis');\n const oAuth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);\n\n const { tokens } = await oAuth2Client.getToken(code as string);\n oAuth2Client.setCredentials(tokens);\n\n // Get User Info\n const oauth2 = google.oauth2({ version: 'v2', auth: oAuth2Client });\n const userInfo = await oauth2.userinfo.get();\n\n // Save Account\n const existing = await StorageAccount.findOne({ owner, 'metadata.google.email': userInfo.data.email, 'metadata.provider': 'GOOGLE' });\n if (existing) {\n existing.metadata.google.credentials = tokens;\n existing.markModified('metadata');\n await existing.save();\n } else {\n await StorageAccount.create({\n owner,\n name: userInfo.data.name || 'Google Drive',\n metadata: {\n provider: 'GOOGLE',\n google: {\n email: userInfo.data.email,\n credentials: tokens\n }\n }\n });\n }\n\n // Helper: Return HTML that closes popup\n res.setHeader('Content-Type', 'text/html');\n return res.send('<script>window.opener.postMessage(\"oauth-success\", \"*\"); window.close();</script>');\n }\n case 'listAccounts': {\n const accounts = await StorageAccount.find({ owner });\n return res.status(200).json({\n status: 200,\n data: {\n accounts: accounts.map(a => ({\n id: a._id.toString(),\n name: a.name,\n email: a.metadata.google?.email || '',\n provider: a.metadata.provider\n }))\n }\n });\n }\n case 'removeAccount': {\n const { id } = req.query;\n const account = await StorageAccount.findOne({ _id: id, owner });\n if (!account) return res.status(404).json({ status: 404, message: 'Account not found' });\n\n // Revoke Token if Google\n if (account.metadata.provider === 'GOOGLE') {\n try {\n await GoogleDriveProvider.revokeToken(owner, account._id.toString());\n } catch (e) {\n console.error('Failed to revoke Google token:', e);\n // Proceed to delete anyway\n }\n }\n\n await StorageAccount.deleteOne({ _id: id, owner });\n await Drive.deleteMany({ owner, storageAccountId: id });\n return res.status(200).json({ status: 200, message: 'Account removed' });\n }\n }\n }\n\n // ** Provider Actions\n const { provider, accountId } = await getProvider(req, owner);\n\n switch (action) {\n // ** 1. LIST **\n case 'list': {\n if (req.method !== 'GET') return res.status(405).json({ status: 405, message: 'Only GET allowed' });\n const listQuery = schemas.listQuerySchema.safeParse(req.query);\n if (!listQuery.success) return res.status(400).json({ status: 400, message: 'Invalid parameters' });\n\n const { folderId, limit, afterId } = listQuery.data;\n\n // Sync Trigger: If viewing a folder, try to sync it first if provider supports it\n // Only sync if we are browsing a specific folder or root\n try {\n await provider.sync(folderId || 'root', owner, accountId);\n } catch (e) {\n console.error('Sync failed', e);\n // Continue to list what we have in DB\n }\n\n // Query DB\n const query: Record<string, unknown> = {\n owner,\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n parentId: folderId === 'root' || !folderId ? null : folderId,\n trashedAt: null\n };\n if (afterId) query._id = { $lt: afterId }; // Pagination\n\n const items = await Drive.find(query, {}, { sort: { order: 1, _id: -1 }, limit });\n const plainItems = await Promise.all(items.map(item => item.toClient()));\n\n res.status(200).json({ status: 200, message: 'Items retrieved', data: { items: plainItems, hasMore: items.length === limit } });\n return;\n }\n\n // ** 2. SEARCH **\n case 'search': {\n const searchData = schemas.searchQuerySchema.safeParse(req.query);\n if (!searchData.success) return res.status(400).json({ status: 400, message: 'Invalid params' });\n const { q, folderId, limit, trashed } = searchData.data;\n\n // Sync Search\n if (!trashed) {\n try { await provider.search(q, owner, accountId); } catch (e) { console.error('Search sync failed', e); }\n }\n\n // Query DB\n const query: Record<string, unknown> = {\n owner,\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n trashedAt: trashed ? { $ne: null } : null,\n name: { $regex: q, $options: 'i' },\n };\n if (folderId && folderId !== 'root') query.parentId = folderId;\n\n const items = await Drive.find(query, {}, { limit, sort: { createdAt: -1 } });\n const plainItems = await Promise.all(items.map(i => i.toClient()));\n\n return res.status(200).json({ status: 200, message: 'Results', data: { items: plainItems } });\n }\n\n // ** 3. UPLOAD **\n case 'upload': {\n if (req.method !== 'POST') return res.status(405).json({ status: 405, message: 'Only POST allowed' });\n const form = formidable({\n multiples: false,\n maxFileSize: config.security.maxUploadSizeInBytes * 2,\n uploadDir: path.join(STORAGE_PATH, 'temp'),\n keepExtensions: true,\n });\n if (!fs.existsSync(path.join(STORAGE_PATH, 'temp'))) fs.mkdirSync(path.join(STORAGE_PATH, 'temp'), { recursive: true });\n\n const [fields, files] = await new Promise<[formidable.Fields, formidable.Files]>((resolve, reject) => {\n form.parse(req, (err, fields, files) => {\n if (err) reject(err); else resolve([fields, files]);\n });\n });\n\n const cleanupTempFiles = (files: formidable.Files) => {\n Object.values(files).flat().forEach(file => {\n if (file && fs.existsSync(file.filepath)) fs.rmSync(file.filepath, { force: true });\n });\n };\n\n const getString = (f: string[] | string | undefined) => (Array.isArray(f) ? f[0] : f || '');\n const getInt = (f: string[] | string | undefined) => parseInt(getString(f) || '0', 10);\n\n const uploadData = schemas.uploadChunkSchema.safeParse({\n chunkIndex: getInt(fields.chunkIndex),\n totalChunks: getInt(fields.totalChunks),\n driveId: getString(fields.driveId) || undefined,\n fileName: getString(fields.fileName),\n fileSize: getInt(fields.fileSize),\n fileType: getString(fields.fileType),\n folderId: getString(fields.folderId) || undefined,\n });\n\n if (!uploadData.success) {\n cleanupTempFiles(files);\n return res.status(400).json({ status: 400, message: uploadData.error.errors[0].message });\n }\n\n const { chunkIndex, totalChunks, driveId, fileName, fileSize: fileSizeInBytes, fileType, folderId } = uploadData.data;\n\n let currentUploadId = driveId;\n\n // Ensure temp directory for this upload exists\n const tempBaseDir = path.join(STORAGE_PATH, 'temp', 'uploads');\n\n if (!currentUploadId) {\n // Start of new upload (usually Chunk 0, but could be adapted)\n if (chunkIndex !== 0) return res.status(400).json({ message: 'Missing upload ID for non-zero chunk' });\n\n if (fileType && !validateMimeType(fileType, config.security.allowedMimeTypes)) {\n cleanupTempFiles(files);\n return res.status(400).json({ status: 400, message: `File type ${fileType} not allowed` });\n }\n\n // Quota Check\n const quota = await provider.getQuota(owner, accountId);\n if (quota.usedInBytes + fileSizeInBytes > quota.quotaInBytes) {\n cleanupTempFiles(files);\n return res.status(413).json({ status: 413, message: 'Storage quota exceeded' });\n }\n\n // Generate Temp ID (Stateless)\n currentUploadId = crypto.randomUUID();\n const uploadDir = path.join(tempBaseDir, currentUploadId);\n fs.mkdirSync(uploadDir, { recursive: true });\n\n // Save Metadata\n const metadata = {\n owner,\n accountId,\n providerName: provider.name,\n name: fileName,\n parentId: folderId === 'root' || !folderId ? null : folderId,\n fileSize: fileSizeInBytes,\n mimeType: fileType,\n totalChunks\n };\n fs.writeFileSync(path.join(uploadDir, 'metadata.json'), JSON.stringify(metadata));\n }\n\n // Handle Chunk Save\n if (currentUploadId) {\n const uploadDir = path.join(tempBaseDir, currentUploadId);\n\n if (!fs.existsSync(uploadDir)) {\n cleanupTempFiles(files);\n // If dir missing, maybe upload expired or finished?\n return res.status(404).json({ status: 404, message: 'Upload session not found or expired' });\n }\n\n try {\n const chunkFile = Array.isArray(files.chunk) ? files.chunk[0] : files.chunk;\n if (!chunkFile) throw new Error('No chunk file received');\n\n // Save part file: part_0, part_1...\n const partPath = path.join(uploadDir, `part_${chunkIndex}`);\n fs.renameSync(chunkFile.filepath, partPath);\n\n // Check Completion\n // We count files starting with 'part_'\n const uploadedParts = fs.readdirSync(uploadDir).filter(f => f.startsWith('part_'));\n\n // Merge if all parts present\n if (uploadedParts.length === totalChunks) {\n // 1. Read Metadata\n const metaPath = path.join(uploadDir, 'metadata.json');\n const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));\n\n // 2. Merge Parts\n const finalTempPath = path.join(uploadDir, 'final.bin');\n const writeStream = fs.createWriteStream(finalTempPath);\n\n for (let i = 0; i < totalChunks; i++) {\n const pPath = path.join(uploadDir, `part_${i}`);\n const data = fs.readFileSync(pPath);\n writeStream.write(data);\n // Optional: delete part immediately to save space? \n // fs.unlinkSync(pPath); \n }\n writeStream.end();\n\n // Wait for stream finish? Sync write above is blocking so it's fine.\n\n // 3. Create DB Record (Delayed)\n const drive = new Drive({\n owner: meta.owner,\n storageAccountId: meta.accountId || null,\n provider: { type: meta.providerName },\n name: meta.name,\n parentId: meta.parentId,\n order: 0,\n information: { type: 'FILE', sizeInBytes: meta.fileSize, mime: meta.mimeType, path: '' }, // path set by provider\n status: 'UPLOADING',\n currentChunk: totalChunks,\n totalChunks: totalChunks,\n });\n\n // Set initial path based on ID - providers will resolve final path/ID\n if (meta.providerName === 'LOCAL' && drive.information.type === 'FILE') {\n drive.information.path = path.join('drive', String(drive._id), 'data.bin');\n }\n\n await drive.save();\n\n // 4. Provider Upload\n try {\n const item = await provider.uploadFile(drive, finalTempPath, meta.accountId);\n\n // Cleanup\n fs.rmSync(uploadDir, { recursive: true, force: true });\n\n const newQuota = await provider.getQuota(meta.owner, meta.accountId);\n res.status(200).json({ status: 200, message: 'Upload complete', data: { type: 'UPLOAD_COMPLETE', driveId: String(drive._id), item }, statistic: { storage: newQuota } });\n\n } catch (err) {\n // Upload to provider failed\n await Drive.deleteOne({ _id: drive._id });\n throw err;\n }\n\n } else {\n // Chunk received, wait for others\n const newQuota = await provider.getQuota(owner, accountId);\n // If this was chunk 0, we send UPLOAD_STARTED with the new ID\n if (chunkIndex === 0) {\n res.status(200).json({ status: 200, message: 'Upload started', data: { type: 'UPLOAD_STARTED', driveId: currentUploadId }, statistic: { storage: newQuota } });\n } else {\n res.status(200).json({ status: 200, message: 'Chunk received', data: { type: 'CHUNK_RECEIVED', driveId: currentUploadId, chunkIndex }, statistic: { storage: newQuota } });\n }\n }\n\n } catch (e) {\n cleanupTempFiles(files);\n // Don't delete entire dir on one chunk fail, might be retryable?\n // For now, if fatal error, maybe we should?\n // Let's just throw for now.\n throw e;\n }\n return;\n }\n\n // Should not happen if logic holds\n cleanupTempFiles(files);\n return res.status(400).json({ status: 400, message: 'Invalid upload request' });\n }\n\n // ** 4. CREATE FOLDER **\n case 'createFolder': {\n const folderData = schemas.createFolderBodySchema.safeParse(req.body);\n if (!folderData.success) return res.status(400).json({ status: 400, message: folderData.error.errors[0].message });\n const { name, parentId } = folderData.data;\n\n const item = await provider.createFolder(name, parentId ?? null, owner, accountId);\n return res.status(201).json({ status: 201, message: 'Folder created', data: { item } });\n }\n\n // ** 5. DELETE **\n case 'delete': {\n const deleteData = schemas.deleteQuerySchema.safeParse(req.query);\n if (!deleteData.success) return res.status(400).json({ status: 400, message: 'Invalid ID' });\n // We use generic delete (trash)\n const { id } = deleteData.data;\n const drive = await Drive.findById(id);\n if (!drive) return res.status(404).json({ status: 404, message: 'Not found' });\n\n // Call Provider Trash\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n\n try {\n await itemProvider.trash([id], owner, itemAccountId);\n } catch (e) {\n console.error('Provider trash failed:', e);\n }\n\n drive.trashedAt = new Date();\n await drive.save();\n return res.status(200).json({ status: 200, message: 'Moved to trash', data: null });\n }\n\n // ** 6. HARD DELETE **\n case 'deletePermanent': {\n const deleteData = schemas.deleteQuerySchema.safeParse(req.query);\n if (!deleteData.success) return res.status(400).json({ status: 400, message: 'Invalid ID' });\n const { id } = deleteData.data;\n // Provider Delete\n await provider.delete([id], owner, accountId);\n const quota = await provider.getQuota(owner, accountId);\n return res.status(200).json({ status: 200, message: 'Deleted', statistic: { storage: quota } });\n }\n\n // ** 7. QUOTA **\n case 'quota': {\n const quota = await provider.getQuota(owner, accountId);\n return res.status(200).json({\n status: 200,\n message: 'Quota retrieved',\n data: { usedInBytes: quota.usedInBytes, totalInBytes: quota.quotaInBytes, availableInBytes: Math.max(0, quota.quotaInBytes - quota.usedInBytes), percentage: quota.quotaInBytes > 0 ? Math.round((quota.usedInBytes / quota.quotaInBytes) * 100) : 0 },\n statistic: { storage: quota },\n });\n }\n\n // ** 7B. TRASH **\n case 'trash': {\n // Try to sync trash first\n try {\n const { provider: trashProvider, accountId: trashAccountId } = await getProvider(req, owner);\n await trashProvider.syncTrash(owner, trashAccountId);\n } catch (e) {\n console.error('Trash sync failed', e);\n }\n\n // Return items that are in trash\n const query: Record<string, unknown> = {\n owner,\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n trashedAt: { $ne: null }\n };\n\n const items = await Drive.find(query, {}, { sort: { trashedAt: -1 } });\n const plainItems = await Promise.all(items.map(item => item.toClient()));\n\n return res.status(200).json({\n status: 200,\n message: 'Trash items',\n data: { items: plainItems, hasMore: false }\n });\n }\n\n // ** 7C. RESTORE **\n case 'restore': {\n const restoreData = schemas.deleteQuerySchema.safeParse(req.query);\n if (!restoreData.success) return res.status(400).json({ status: 400, message: 'Invalid ID' });\n const { id } = restoreData.data;\n const drive = await Drive.findById(id);\n if (!drive) return res.status(404).json({ status: 404, message: 'Not found' });\n\n // Check if parent folder is trashed - if so, move to root\n let targetParentId = drive.parentId;\n if (targetParentId) {\n const parent = await Drive.findById(targetParentId);\n if (parent?.trashedAt) {\n targetParentId = null; // Move to root instead\n }\n }\n\n // Call Provider Untrash\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n\n try {\n await itemProvider.untrash([id], owner, itemAccountId);\n // If moving to root due to trashed parent, update location in provider\n if (targetParentId !== drive.parentId) {\n await itemProvider.move(id, targetParentId?.toString() ?? null, owner, itemAccountId);\n }\n } catch (e) {\n console.error('Provider restore failed:', e);\n }\n\n // Restore item in database\n drive.trashedAt = null;\n drive.parentId = targetParentId;\n await drive.save();\n\n return res.status(200).json({\n status: 200,\n message: targetParentId === null && drive.parentId !== null\n ? 'Restored to root (parent folder was trashed)'\n : 'Restored',\n data: null\n });\n }\n\n // ** 7D. MOVE **\n case 'move': {\n const moveData = schemas.moveBodySchema.safeParse(req.body);\n if (!moveData.success) return res.status(400).json({ status: 400, message: 'Invalid data' });\n const { ids, targetFolderId } = moveData.data;\n\n const items: TDatabaseDrive[] = [];\n // Target folder ID for provider (null for root)\n const effectiveTargetId = targetFolderId === 'root' || !targetFolderId ? null : targetFolderId;\n\n for (const id of ids) {\n try {\n const item = await provider.move(id, effectiveTargetId, owner, accountId);\n items.push(item);\n } catch (e) {\n console.error(`Failed to move item ${id}`, e);\n // Continue moving other items\n }\n }\n\n return res.status(200).json({ status: 200, message: 'Moved', data: { items } });\n }\n\n // ** 8. RENAME **\n case 'rename': {\n const renameData = schemas.renameBodySchema.safeParse({ id: req.query.id, ...req.body });\n if (!renameData.success) return res.status(400).json({ status: 400, message: 'Invalid data' });\n const { id, newName } = renameData.data;\n const item = await provider.rename(id, newName, owner, accountId);\n return res.status(200).json({ status: 200, message: 'Renamed', data: { item } });\n }\n\n // ** 9. THUMBNAIL **\n case 'thumbnail': {\n const thumbQuery = schemas.thumbnailQuerySchema.safeParse(req.query);\n if (!thumbQuery.success) return res.status(400).json({ status: 400, message: 'Invalid params' });\n const { id } = thumbQuery.data;\n const drive = await Drive.findById(id);\n if (!drive) return res.status(404).json({ status: 404, message: 'Not found' });\n\n // Resolve Correct Provider\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n\n const stream = await itemProvider.getThumbnail(drive, itemAccountId);\n res.setHeader('Content-Type', 'image/webp');\n stream.pipe(res);\n return;\n }\n\n // ** 10. SERVE / DOWNLOAD **\n case 'serve': {\n const serveQuery = schemas.serveQuerySchema.safeParse(req.query);\n if (!serveQuery.success) return res.status(400).json({ status: 400, message: 'Invalid params' });\n const { id } = serveQuery.data;\n const drive = await Drive.findById(id);\n if (!drive) return res.status(404).json({ status: 404, message: 'Not found' });\n\n // Resolve Correct Provider\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n\n const { stream, mime, size } = await itemProvider.openStream(drive, itemAccountId);\n\n const safeFilename = sanitizeContentDispositionFilename(drive.name);\n res.setHeader('Content-Disposition', `inline; filename=\"${safeFilename}\"`);\n res.setHeader('Content-Type', mime);\n // Google streams might not give exact size, but we have IT in DB?\n if (size) res.setHeader('Content-Length', size);\n\n stream.pipe(res);\n return;\n }\n\n default:\n res.status(400).json({ status: 400, message: `Unknown action: ${action}` });\n }\n } catch (error: unknown) {\n console.error(`[next-file-manager] Error handling action ${action}:`, error);\n // FOR DEBUGGING: Return the actual error message\n res.status(500).json({ status: 500, message: error instanceof Error ? error.message : 'Unknown error' });\n }\n};\n\n// ** Exports\nexport { driveConfiguration, getDriveConfig, getDriveInformation };\nexport { driveGetUrl, driveReadFile, driveFilePath } from '@/server/controllers/drive';\nexport type { TDriveFile } from '@/types/client';\nexport type * from '@/types/server';\n","// ** Server Config Wrapper\nimport type { NextApiRequest } from 'next';\nimport mongoose from 'mongoose';\nimport type { TDriveConfiguration, TDriveConfigInformation } from '@/types/server';\n\nlet globalConfig: TDriveConfiguration | null = null;\n\n// ** Initialize configuration\nexport const driveConfiguration = (config: TDriveConfiguration): TDriveConfiguration => {\n // Check database connection\n if (mongoose.connection.readyState !== 1) {\n throw new Error('Database not connected. Please connect to Mongoose before initializing next-file-manager.');\n }\n\n // Apply default values if not provided in the config\n const mergedConfig: TDriveConfiguration = {\n ...config,\n security: {\n maxUploadSizeInBytes: config.security?.maxUploadSizeInBytes ?? 10 * 1024 * 1024, // Default to 10MB\n allowedMimeTypes: config.security?.allowedMimeTypes ?? ['*/*'],\n signedUrls: config.security?.signedUrls,\n trash: config.security?.trash\n },\n information: config.information ?? (async (req) => {\n return {\n key: { id: 'default-user' },\n storage: { quotaInBytes: 10 * 1024 * 1024 * 1024 } // Default to 10GB\n };\n }),\n };\n\n globalConfig = mergedConfig;\n return mergedConfig;\n};\n\n// ** Get current configuration\nexport const getDriveConfig = (): TDriveConfiguration => {\n if (!globalConfig) throw new Error('Drive configuration not initialized');\n return globalConfig;\n};\n\n// ** Get drive information (quota, owner)\nexport const getDriveInformation = async (req: NextApiRequest): Promise<TDriveConfigInformation> => {\n const config = getDriveConfig();\n return config.information(req);\n};\n","// ** Mongoose Drive Schema\nimport type {\n TDatabaseDrive,\n TDatabaseDriveInformation,\n TDatabaseDriveMetadata,\n TDatabaseDriveProvider,\n TDatabaseDriveStatus,\n} from '@/types/lib/database/drive';\nimport mongoose, { Schema, type Document, type Model, type Types } from 'mongoose';\n\nexport interface IDatabaseDriveDocument extends Document {\n owner: Record<string, unknown> | null;\n storageAccountId: Types.ObjectId | null;\n name: string;\n parentId: Types.ObjectId | null;\n order: number;\n provider: TDatabaseDriveProvider;\n metadata: TDatabaseDriveMetadata;\n information: TDatabaseDriveInformation;\n status: TDatabaseDriveStatus;\n trashedAt: Date | null;\n createdAt: Date;\n\n toClient: () => Promise<TDatabaseDrive>;\n}\n\n// ** Schema definition\nconst informationSchema = new Schema({\n type: { type: String, enum: ['FILE', 'FOLDER'], required: true },\n sizeInBytes: { type: Number, default: 0 },\n mime: { type: String },\n path: { type: String },\n width: { type: Number },\n height: { type: Number },\n duration: { type: Number },\n hash: { type: String },\n}, { _id: false });\n\n// Provider Sub-schema to handle 'type' field correctly without ambiguity\nconst providerSchema = new Schema({\n type: { type: String, enum: ['LOCAL', 'GOOGLE'], required: true, default: 'LOCAL' },\n google: { type: Schema.Types.Mixed }\n}, { _id: false });\n\nconst DriveSchema: Schema = new Schema<IDatabaseDriveDocument>(\n {\n owner: { type: Schema.Types.Mixed, default: null },\n storageAccountId: { type: Schema.Types.ObjectId, ref: 'StorageAccount', default: null },\n name: { type: String, required: true },\n parentId: { type: Schema.Types.ObjectId, ref: 'Drive', default: null },\n order: { type: Number, default: 0 },\n provider: { type: providerSchema, default: () => ({ type: 'LOCAL' }) },\n metadata: { type: Schema.Types.Mixed, default: {} },\n information: { type: informationSchema, required: true },\n status: { type: String, enum: ['READY', 'PROCESSING', 'UPLOADING', 'FAILED'], default: 'PROCESSING' },\n trashedAt: { type: Date, default: null },\n createdAt: { type: Date, default: Date.now },\n },\n { minimize: false },\n);\n\n// ** Indexes\nDriveSchema.index({ owner: 1, 'information.type': 1 });\nDriveSchema.index({ owner: 1, 'provider.type': 1, 'provider.google.id': 1 }); // Provider lookup updated\nDriveSchema.index({ owner: 1, storageAccountId: 1 });\nDriveSchema.index({ owner: 1, trashedAt: 1 });\nDriveSchema.index({ owner: 1, 'information.hash': 1 });\nDriveSchema.index({ owner: 1, name: 'text' });\nDriveSchema.index({ owner: 1, 'provider.type': 1 });\n\n// ** Method: toClient\nDriveSchema.method<IDatabaseDriveDocument>('toClient', async function (): Promise<TDatabaseDrive> {\n const data = this.toJSON<IDatabaseDriveDocument>();\n\n return {\n id: String(data._id),\n name: data.name,\n parentId: data.parentId ? String(data.parentId) : null,\n order: data.order,\n provider: data.provider,\n metadata: data.metadata,\n information: data.information,\n status: data.status,\n trashedAt: data.trashedAt,\n createdAt: data.createdAt,\n };\n});\n\nconst Drive: Model<IDatabaseDriveDocument> = mongoose.models.Drive || mongoose.model<IDatabaseDriveDocument>('Drive', DriveSchema);\n\nexport default Drive;\n","// ** Mongoose Storage Account Schema\nimport type { TDatabaseStorageAccount, TDatabaseStorageAccountMetadata } from '@/types/lib/database/storage/account';\nimport mongoose, { Schema, type Document, type Model, type Types } from 'mongoose';\n\nexport interface IDatabaseStorageAccountDocument extends Document {\n owner: Record<string, unknown> | null;\n name: string;\n metadata: TDatabaseStorageAccountMetadata;\n createdAt: Date;\n\n toClient: () => Promise<TDatabaseStorageAccount>;\n}\n\n// ** Schema definition\nconst StorageAccountSchema: Schema = new Schema<IDatabaseStorageAccountDocument>(\n {\n owner: { type: Schema.Types.Mixed, default: null },\n name: { type: String, required: true },\n metadata: {\n provider: { type: String, enum: ['GOOGLE'], required: true },\n google: {\n email: { type: String, required: true },\n credentials: { type: Schema.Types.Mixed, required: true },\n },\n },\n createdAt: { type: Date, default: Date.now },\n },\n { minimize: false }\n);\n\n// ** Indexes\nStorageAccountSchema.index({ owner: 1, 'metadata.provider': 1 });\nStorageAccountSchema.index({ owner: 1, 'metadata.google.email': 1 });\n\n// ** Method: toClient\nStorageAccountSchema.method<IDatabaseStorageAccountDocument>('toClient', async function (): Promise<TDatabaseStorageAccount> {\n const data = this.toJSON<IDatabaseStorageAccountDocument>();\n\n return {\n id: String(data._id),\n owner: data.owner,\n name: data.name,\n metadata: data.metadata,\n createdAt: data.createdAt,\n };\n});\n\nconst StorageAccount: Model<IDatabaseStorageAccountDocument> = mongoose.models.StorageAccount || mongoose.model<IDatabaseStorageAccountDocument>('StorageAccount', StorageAccountSchema);\n\nexport default StorageAccount;\n","import fs from 'fs';\nimport crypto from 'crypto';\nimport sharp from 'sharp';\n\nexport const QUALITY_MAP: Record<string, number> = {\n\tultralow: 10,\n\tlow: 30,\n\tmedium: 60,\n\thigh: 80,\n\tnormal: 100,\n};\n\nexport const isImageMimeType = (mime: string): boolean => ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/avif'].includes(mime);\n\nexport const validateMimeType = (mime: string, allowedTypes: string[]): boolean => {\n\tif (allowedTypes.includes('*/*')) return true;\n\treturn allowedTypes.some(pattern => {\n\t\tif (pattern === mime) return true;\n\t\tif (pattern.endsWith('/*')) {\n\t\t\tconst prefix = pattern.slice(0, -2);\n\t\t\treturn mime.startsWith(`${prefix}/`);\n\t\t}\n\t\treturn false;\n\t});\n};\n\nexport const computeFileHash = (filePath: string): Promise<string> =>\n\tnew Promise((resolve, reject) => {\n\t\tconst hash = crypto.createHash('sha256');\n\t\tconst stream = fs.createReadStream(filePath);\n\t\tstream.on('data', data => hash.update(data));\n\t\tstream.on('end', () => resolve(hash.digest('hex')));\n\t\tstream.on('error', reject);\n\t});\n\nexport const extractImageMetadata = async (filePath: string) => {\n\ttry {\n\t\tconst { width = 0, height = 0, exif } = await sharp(filePath).metadata();\n\t\treturn { width, height, ...(exif && { exif: { raw: exif.toString('base64') } }) };\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nexport const formatFileSize = (bytes: number): string => {\n\tif (bytes === 0) return '0 B';\n\tconst k = 1024;\n\tconst sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n\tconst i = Math.floor(Math.log(bytes) / Math.log(k));\n\treturn `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n};\n\nexport const ownerMatches = (a: Record<string, unknown> | null, b: Record<string, unknown> | null): boolean => {\n\tif (a === null && b === null) return true;\n\tif (a === null || b === null) return false;\n\treturn JSON.stringify(a) === JSON.stringify(b);\n};\n","import { z } from 'zod';\r\nimport { isValidObjectId } from 'mongoose';\r\n\r\n// ** Custom ObjectId validator\r\nconst objectIdSchema = z.string().refine(val => isValidObjectId(val), {\r\n message: 'Invalid ObjectId format',\r\n});\r\n\r\n// ** Sanitize filename - remove path traversal and dangerous characters\r\nconst sanitizeFilename = (name: string): string => {\r\n return (\r\n name\r\n .replace(/[<>:\"|?*\\x00-\\x1F]/g, '') // Remove dangerous chars\r\n .replace(/^\\.+/, '') // Remove leading dots\r\n .replace(/\\.+$/, '') // Remove trailing dots\r\n .replace(/\\\\/g, '/') // Normalize slashes\r\n .replace(/\\/+/g, '/') // Remove duplicate slashes\r\n .replace(/\\.\\.\\//g, '') // Remove path traversal\r\n .replace(/\\.\\.+/g, '') // Remove remaining ..\r\n .split('/')\r\n .pop() ||\r\n '' // Take only the filename part (remove all paths)\r\n .trim()\r\n .slice(0, 255)\r\n ); // Limit length\r\n};\r\n\r\n// ** Sanitize search query for regex\r\nconst sanitizeRegexInput = (input: string): string => {\r\n // Escape regex special characters\r\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&').slice(0, 100);\r\n};\r\n\r\n// ** File/Folder name schema\r\nconst nameSchema = z\r\n .string()\r\n .min(1, 'Name is required')\r\n .max(255, 'Name too long')\r\n .transform(sanitizeFilename)\r\n .refine(val => val.length > 0, { message: 'Invalid name after sanitization' });\r\n\r\n// ** Upload chunk schema\r\nexport const uploadChunkSchema = z\r\n .object({\r\n chunkIndex: z.number().int().min(0).max(10000),\r\n totalChunks: z.number().int().min(1).max(10000),\r\n driveId: z.string().optional(),\r\n fileName: nameSchema,\r\n fileSize: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),\r\n fileType: z.string().min(1).max(255),\r\n folderId: z.string().optional(),\r\n })\r\n .refine(data => data.chunkIndex < data.totalChunks, {\r\n message: 'Chunk index must be less than total chunks',\r\n });\r\n\r\n// ** List query schema\r\nexport const listQuerySchema = z.object({\r\n folderId: z.union([z.literal('root'), objectIdSchema, z.undefined()]),\r\n limit: z\r\n .string()\r\n .optional()\r\n .transform(val => {\r\n const num = parseInt(val || '50', 10);\r\n return Math.min(Math.max(1, num), 100);\r\n }),\r\n afterId: objectIdSchema.optional(),\r\n});\r\n\r\n// ** Serve query schema\r\nexport const serveQuerySchema = z.object({\r\n id: objectIdSchema,\r\n token: z.string().optional(),\r\n q: z.enum(['ultralow', 'low', 'medium', 'high', 'normal']).optional(),\r\n format: z.enum(['webp', 'jpeg', 'png']).optional(),\r\n});\r\n\r\n// ** Thumbnail query schema\r\nexport const thumbnailQuerySchema = z.object({\r\n id: objectIdSchema,\r\n size: z.enum(['small', 'medium', 'large']).optional().default('medium'),\r\n token: z.string().optional(),\r\n});\r\n\r\n// ** Rename body schema\r\nexport const renameBodySchema = z.object({\r\n id: objectIdSchema,\r\n newName: nameSchema,\r\n});\r\n\r\n// ** Delete query schema\r\nexport const deleteQuerySchema = z.object({\r\n id: objectIdSchema,\r\n});\r\n\r\n// ** Delete many body schema\r\nexport const deleteManyBodySchema = z.object({\r\n ids: z.array(objectIdSchema).min(1).max(1000),\r\n});\r\n\r\n// ** Create folder body schema\r\nexport const createFolderBodySchema = z.object({\r\n name: nameSchema,\r\n parentId: z.union([z.literal('root'), objectIdSchema, z.string().length(0), z.undefined()]).optional(),\r\n});\r\n\r\n// ** Move body schema\r\nexport const moveBodySchema = z.object({\r\n ids: z.array(objectIdSchema).min(1).max(1000),\r\n targetFolderId: z.union([z.literal('root'), objectIdSchema, z.undefined()]).optional(),\r\n});\r\n\r\n// ** Reorder body schema\r\nexport const reorderBodySchema = z.object({\r\n ids: z.array(objectIdSchema).min(1).max(1000),\r\n});\r\n\r\n// ** Search query schema\r\nexport const searchQuerySchema = z.object({\r\n q: z.string().min(1).max(100).transform(sanitizeRegexInput),\r\n folderId: z.union([z.literal('root'), objectIdSchema, z.undefined()]).optional(),\r\n limit: z\r\n .string()\r\n .optional()\r\n .transform(val => {\r\n const num = parseInt(val || '50', 10);\r\n return Math.min(Math.max(1, num), 100);\r\n }),\r\n trashed: z\r\n .string()\r\n .optional()\r\n .transform(val => val === 'true'),\r\n});\r\n\r\n// ** Restore query schema\r\nexport const restoreQuerySchema = z.object({\r\n id: objectIdSchema,\r\n});\r\n\r\n// ** Cancel query schema\r\nexport const cancelQuerySchema = z.object({\r\n id: objectIdSchema,\r\n});\r\n\r\n// ** Purge trash query schema\r\nexport const purgeTrashQuerySchema = z.object({\r\n days: z.number().int().min(1).max(365).optional(),\r\n});\r\n\r\n// ** Drive File Schema (Public)\r\nexport const driveFileSchemaZod = z.object({\r\n id: z.string(),\r\n file: z.object({\r\n name: z.string(),\r\n mime: z.string(),\r\n size: z.number(),\r\n }),\r\n});\r\n\r\n// ** Constants\r\nexport const MAX_FOLDER_DEPTH = 50;\r\n\r\n// ** Export sanitization functions\r\nexport { sanitizeFilename, sanitizeRegexInput };\r\n","import crypto from 'crypto';\r\n\r\n/**\r\n * Constant-time string comparison to prevent timing attacks\r\n */\r\nexport function constantTimeCompare(a: string, b: string): boolean {\r\n\tif (a.length !== b.length) {\r\n\t\treturn false;\r\n\t}\r\n\r\n\tlet result = 0;\r\n\tfor (let i = 0; i < a.length; i++) {\r\n\t\tresult |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n\t}\r\n\r\n\treturn result === 0;\r\n}\r\n\r\n/**\r\n * Safe error message that doesn't leak sensitive information\r\n */\r\nexport function getSafeErrorMessage(error: unknown): string {\r\n\tif (error instanceof Error) {\r\n\t\t// Filter out sensitive patterns (case-insensitive)\r\n\t\tconst message = error.message.toLowerCase();\r\n\r\n\t\t// Don't expose database errors\r\n\t\tif (message.includes('mongo')) {\r\n\t\t\treturn 'Database operation failed';\r\n\t\t}\r\n\r\n\t\t// Don't expose file paths\r\n\t\tif (message.includes('/') || message.includes('\\\\')) {\r\n\t\t\treturn 'File operation failed';\r\n\t\t}\r\n\r\n\t\t// Don't expose validation details from internal libraries\r\n\t\tif (message.includes('validation') || message.includes('cast')) {\r\n\t\t\treturn 'Invalid input';\r\n\t\t}\r\n\r\n\t\t// Generic safe messages\r\n\t\treturn 'Operation failed';\r\n\t}\r\n\r\n\treturn 'Internal server error';\r\n}\r\n\r\n/**\r\n * Validate and sanitize Content-Disposition filename\r\n */\r\nexport function sanitizeContentDispositionFilename(filename: string): string {\r\n\t// Remove any path components\r\n\tconst basename = filename.replace(/^.*[\\\\\\/]/, '');\r\n\r\n\t// Remove or encode dangerous characters\r\n\treturn basename\r\n\t\t.replace(/[\"\\r\\n]/g, '')\r\n\t\t.replace(/[^\\x20-\\x7E]/g, '') // Remove non-printable ASCII\r\n\t\t.slice(0, 255);\r\n}\r\n","import fs from 'fs';\nimport path from 'path';\nimport mongoose from 'mongoose';\nimport Drive from '@/server/database/mongoose/schema/drive';\nimport { getDriveConfig } from '@/server/config';\nimport { extractImageMetadata, computeFileHash } from '@/server/utils';\nimport type { TStorageProvider } from '@/types/server/storage';\nimport type { IDatabaseDriveDocument } from '@/server/database/mongoose/schema/drive';\nimport ffmpeg from 'fluent-ffmpeg';\n\nconst STORAGE_PATH = path.join(process.cwd(), 'storage');\n\nexport const LocalStorageProvider: TStorageProvider = {\n name: 'LOCAL',\n\n sync: async (folderId, owner, accountId) => {\n // No-op for local storage as DB is the source of truth\n },\n\n search: async (query, owner, accountId) => {\n // No-op for local storage as DB text search handles it\n },\n\n getQuota: async (owner, accountId) => {\n // Calculate storage used from DB\n const result = await Drive.aggregate([\n { $match: { owner, 'information.type': 'FILE', trashedAt: null } },\n { $group: { _id: null, total: { $sum: '$information.sizeInBytes' } } }\n ]);\n const usedInBytes = result[0]?.total || 0;\n\n // Get quota limit from config/info\n // Since we don't have user info passed directly, we default to config or infinite\n // Note: The original implementation fetched this from `getDriveInformation` in config\n // access via config for now\n const config = getDriveConfig();\n // Assuming default quota if not found, or infinite\n return { usedInBytes, quotaInBytes: 10 * 1024 * 1024 * 1024 }; // Default 10GB for now, or fetch dynamically if needed\n },\n\n openStream: async (item: IDatabaseDriveDocument, accountId?: string) => {\n if (item.information.type !== 'FILE') throw new Error('Cannot stream folder');\n const filePath = path.join(STORAGE_PATH, item.information.path);\n\n if (!fs.existsSync(filePath)) {\n throw new Error('File not found on disk');\n }\n\n const stat = fs.statSync(filePath);\n const stream = fs.createReadStream(filePath);\n\n return {\n stream,\n mime: item.information.mime,\n size: stat.size\n };\n },\n\n getThumbnail: async (item: IDatabaseDriveDocument, accountId?: string) => {\n if (item.information.type !== 'FILE') throw new Error('No thumbnail for folder');\n\n const originalPath = path.join(STORAGE_PATH, item.information.path);\n const thumbPath = path.join(STORAGE_PATH, 'cache', 'thumbnails', `${item._id.toString()}.webp`);\n\n if (!fs.existsSync(originalPath)) throw new Error('Original file not found');\n\n if (fs.existsSync(thumbPath)) {\n return fs.createReadStream(thumbPath);\n }\n\n // Generate thumbnail\n if (!fs.existsSync(path.dirname(thumbPath))) fs.mkdirSync(path.dirname(thumbPath), { recursive: true });\n\n if (item.information.mime.startsWith('image/')) {\n const sharp = require('sharp'); // Dynamic import to avoid issues if not installed, though it is\n await sharp(originalPath)\n .resize(300, 300, { fit: 'inside' })\n .toFormat('webp', { quality: 80 })\n .toFile(thumbPath);\n } else if (item.information.mime.startsWith('video/')) {\n await new Promise((resolve, reject) => {\n ffmpeg(originalPath)\n .screenshots({\n count: 1,\n folder: path.dirname(thumbPath),\n filename: path.basename(thumbPath),\n size: '300x?'\n })\n .on('end', resolve)\n .on('error', reject);\n });\n } else {\n throw new Error('Unsupported mime type for thumbnail');\n }\n\n return fs.createReadStream(thumbPath);\n },\n\n createFolder: async (name, parentId, owner, accountId) => {\n // Just DB operation for local folders (virtual)\n const getNextOrderValue = async (owner: Record<string, unknown> | null): Promise<number> => {\n const lastItem = await Drive.findOne({ owner }, {}, { sort: { order: -1 } });\n return lastItem ? lastItem.order + 1 : 0;\n };\n\n const folder = new Drive({\n owner,\n name,\n parentId: parentId === 'root' || !parentId ? null : parentId,\n order: await getNextOrderValue(owner),\n provider: { type: 'LOCAL' },\n information: { type: 'FOLDER' },\n status: 'READY'\n });\n await folder.save();\n return folder.toClient();\n },\n\n uploadFile: async (drive, filePath, accountId) => {\n if (drive.information.type !== 'FILE') throw new Error('Invalid drive type');\n\n // Move temp file to final destination\n const destPath = path.join(STORAGE_PATH, drive.information.path);\n const dirPath = path.dirname(destPath);\n if (!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true });\n\n // Rename (move)\n fs.renameSync(filePath, destPath);\n\n drive.status = 'READY';\n drive.information.hash = await computeFileHash(destPath);\n\n if (drive.information.mime.startsWith('image/')) {\n const meta = await extractImageMetadata(destPath);\n if (meta) {\n drive.information.width = meta.width;\n drive.information.height = meta.height;\n }\n }\n\n await drive.save();\n return drive.toClient();\n },\n\n delete: async (ids, owner, accountId) => {\n const items = await Drive.find({ _id: { $in: ids }, owner }).lean();\n\n // Helper to recursively get all children\n const getAllChildren = async (folderIds: string[]): Promise<any[]> => {\n const children = await Drive.find({ parentId: { $in: folderIds }, owner }).lean();\n if (children.length === 0) return [];\n\n const subFolderIds = children\n .filter(c => c.information.type === 'FOLDER')\n .map(c => c._id.toString());\n\n const subChildren = await getAllChildren(subFolderIds);\n return [...children, ...subChildren];\n };\n\n const folderIds = items.filter(i => i.information.type === 'FOLDER').map(i => i._id.toString());\n const allChildren = await getAllChildren(folderIds);\n const allItemsToDelete = [...items, ...allChildren];\n\n // Delete files from disk\n for (const item of allItemsToDelete) {\n if (item.information.type === 'FILE' && item.information.path) {\n // Check if any other file points to this path (shouldn't happen in simple model but good hygiene)\n // Actually in this model, path is unique per file\n const fullPath = path.join(STORAGE_PATH, item.information.path);\n const dirPath = path.dirname(fullPath); // .../drive/ID/\n if (fs.existsSync(dirPath)) {\n fs.rmSync(dirPath, { recursive: true, force: true });\n }\n }\n }\n\n // Delete from DB\n await Drive.deleteMany({ _id: { $in: allItemsToDelete.map(i => i._id) } });\n },\n\n trash: async (ids, owner, accountId) => {\n // No-op for local, handled by DB update in index.ts\n },\n\n syncTrash: async (owner, accountId) => {\n // No-op for local\n },\n\n untrash: async (ids, owner, accountId) => {\n // No-op for local\n },\n\n rename: async (id, newName, owner, accountId) => {\n const item = await Drive.findOneAndUpdate(\n { _id: id, owner },\n { name: newName },\n { new: true }\n );\n if (!item) throw new Error('Item not found');\n return item.toClient();\n },\n\n move: async (id, newParentId, owner, accountId) => {\n const item = await Drive.findOne({ _id: id, owner });\n if (!item) throw new Error('Item not found');\n\n // Update DB\n const oldParentId = item.parentId;\n item.parentId = newParentId === 'root' || !newParentId ? null : new mongoose.Types.ObjectId(newParentId);\n\n // For LOCAL, we might store files in flat structure or hierarchical? \n // Looking at uploadFile (lines 124): path.join(STORAGE_PATH, drive.information.path)\n // And line 171: path.join(STORAGE_PATH, item.information.path)\n // And line 374 in index.ts: drive.information.path = path.join('drive', String(drive._id), 'data.bin');\n // It seems path is ID-based: drive/<ID>/data.bin.\n // So moving a file DOES NOT change its physical path on disk if path is ID-based and flat?\n // Let's verify 'createFolder'. It just creates DB entry.\n // So hierarchy is virtual in DB. Physical storage is flat-ish (by ID).\n // IF so, 'move' is just DB update!\n\n // Let's double check implementation of 'uploadFile' in local.ts\n // Line 124: destPath = path.join(STORAGE_PATH, drive.information.path);\n // And 'path' seems to be set in index.ts line 374: `path.join('drive', String(drive._id), 'data.bin')`\n // So yes, physical path is detached from folder structure.\n\n // So for Local, only DB update is needed.\n await item.save();\n return item.toClient();\n },\n\n revokeToken: async (owner, accountId) => {\n // No-op\n }\n};\n","import fs from 'fs';\nimport path from 'path';\nimport { google } from 'googleapis';\nimport { Readable } from 'stream';\nimport mongoose from 'mongoose';\nimport Drive from '@/server/database/mongoose/schema/drive';\nimport StorageAccount from '@/server/database/mongoose/schema/storage/account';\nimport { getDriveConfig } from '@/server/config';\nimport type { TStorageProvider, TDriveQuota } from '@/types/server/storage';\nimport type { IDatabaseDriveDocument } from '@/server/database/mongoose/schema/drive';\nimport type { TDatabaseDrive } from '@/types/lib/database/drive';\n\nconst STORAGE_PATH = path.join(process.cwd(), 'storage');\n\nconst createAuthClient = async (owner: Record<string, unknown> | null, accountId?: string) => {\n // 1. Get credentials from StorageAccount\n const query: any = { owner, 'metadata.provider': 'GOOGLE' };\n if (accountId) query._id = accountId;\n\n // If multiple accounts and no accountId, we might pick a random one? \n // Ideally we should enforce accountId.\n const account = await StorageAccount.findOne(query); // Pick first one if no ID specificied\n if (!account) throw new Error('Google Drive account not connected');\n\n const config = getDriveConfig();\n const { clientId, clientSecret, redirectUri } = config.storage?.google || {};\n\n if (!clientId || !clientSecret) throw new Error('Google credentials not configured on server');\n\n const oAuth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);\n\n // Verify it's a google account and metadata exists\n // Note: StorageAccount schema might still use metadata.provider or just provider?\n // Based on previous grep, context.tsx had { provider: 'GOOGLE' }. \n // Usually StorageAccount keys are consistent.\n // If we only refactored Drive schema, StorageAccount might be unchanged.\n // But `src/server/database/mongoose/schema/drive.ts` was refactored.\n // I need to be careful about StorageAccount.\n // Let's assume StorageAccount is NOT changed in this refactor unless user asked.\n // User only mentioned \"Drive Schema\".\n // But my code in createAuthClient uses account.metadata.provider.\n // Let's keep it as is if StorageAccount wasn't refactored.\n if (account.metadata.provider !== 'GOOGLE' || !account.metadata.google) {\n throw new Error('Invalid Google Account Metadata');\n }\n\n oAuth2Client.setCredentials(account.metadata.google.credentials);\n\n // Update tokens listener\n oAuth2Client.on('tokens', async (tokens) => {\n if (tokens.refresh_token) {\n account.metadata.google.credentials = { ...account.metadata.google.credentials, ...tokens };\n account.markModified('metadata');\n await account.save();\n }\n });\n\n return { client: oAuth2Client, accountId: account._id };\n};\n\nexport const GoogleDriveProvider: TStorageProvider = {\n name: 'GOOGLE',\n\n sync: async (folderId, owner, accountId) => {\n const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n // Resolve Google Folder ID\n let googleParentId = 'root';\n if (folderId && folderId !== 'root') {\n const folder = await Drive.findOne({ _id: folderId, owner });\n if (folder && folder.provider?.google?.id) {\n googleParentId = folder.provider.google.id;\n } else {\n return;\n }\n }\n\n // List files from Google\n // Implement pagination loop\n let nextPageToken: string | undefined = undefined;\n // Keep track of all synced IDs to identify deletions\n const allSyncedGoogleIds = new Set<string>();\n\n do {\n const res: any = await drive.files.list({\n q: `'${googleParentId}' in parents and trashed = false`,\n fields: 'nextPageToken, files(id, name, mimeType, size, webViewLink, iconLink, thumbnailLink)',\n pageSize: 1000,\n pageToken: nextPageToken\n });\n\n nextPageToken = res.data.nextPageToken || undefined;\n const files = res.data.files || [];\n\n // Upsert to MongoDB\n for (const file of files) {\n if (!file.id || !file.name || !file.mimeType) continue;\n\n // Track ID\n allSyncedGoogleIds.add(file.id);\n\n // RELAXED FILTER: Allow Google Docs (application/vnd.google-apps.*)\n const isFolder = file.mimeType === 'application/vnd.google-apps.folder';\n // if (!isFolder && file.mimeType.startsWith('application/vnd.google-apps.')) continue; // REMOVED FILTER\n\n const sizeInBytes = file.size ? parseInt(file.size) : 0;\n\n // Construct update data\n const updateData = {\n name: file.name,\n storageAccountId: foundAccountId,\n parentId: folderId === 'root' ? null : folderId,\n information: {\n type: isFolder ? 'FOLDER' : 'FILE',\n sizeInBytes,\n mime: file.mimeType,\n path: '',\n },\n provider: {\n type: 'GOOGLE',\n google: {\n id: file.id,\n webViewLink: file.webViewLink,\n iconLink: file.iconLink,\n thumbnailLink: file.thumbnailLink\n }\n },\n status: 'READY',\n trashedAt: null\n };\n\n await Drive.findOneAndUpdate(\n {\n owner,\n 'provider.google.id': file.id,\n 'provider.type': 'GOOGLE'\n },\n { $set: updateData },\n { upsert: true, new: true, setDefaultsOnInsert: true }\n );\n }\n } while (nextPageToken);\n\n // Handle deletions - remove items in DB that were NOT in the gathered list\n const dbItems = await Drive.find({\n owner,\n storageAccountId: foundAccountId,\n parentId: folderId === 'root' ? null : folderId,\n 'provider.type': 'GOOGLE'\n });\n\n for (const item of dbItems) {\n // If item has a google ID and it wasn't seen in the sync list -> trash it\n if (item.provider?.google?.id && !allSyncedGoogleIds.has(item.provider.google.id)) {\n item.trashedAt = new Date();\n await item.save();\n }\n }\n },\n\n syncTrash: async (owner, accountId) => {\n const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n // List trashed files from Google\n let nextPageToken: string | undefined = undefined;\n\n do {\n const res: any = await drive.files.list({\n q: 'trashed = true',\n fields: 'nextPageToken, files(id, name, mimeType, size, webViewLink, iconLink, thumbnailLink)',\n pageSize: 100, // Limit sync for performance\n pageToken: nextPageToken\n });\n\n nextPageToken = res.data.nextPageToken || undefined;\n const files = res.data.files || [];\n\n for (const file of files) {\n if (!file.id || !file.name || !file.mimeType) continue;\n\n // RELAXED FILTER: Allow Google Docs (application/vnd.google-apps.*)\n const isFolder = file.mimeType === 'application/vnd.google-apps.folder';\n\n const sizeInBytes = file.size ? parseInt(file.size) : 0;\n\n await Drive.findOneAndUpdate(\n { owner, 'provider.google.id': file.id, 'provider.type': 'GOOGLE' },\n {\n $set: {\n name: file.name,\n storageAccountId: foundAccountId,\n information: {\n type: isFolder ? 'FOLDER' : 'FILE',\n sizeInBytes,\n mime: file.mimeType,\n path: ''\n },\n provider: {\n type: 'GOOGLE',\n google: {\n id: file.id,\n webViewLink: file.webViewLink,\n iconLink: file.iconLink,\n thumbnailLink: file.thumbnailLink\n }\n },\n trashedAt: new Date()\n }\n },\n { upsert: true, setDefaultsOnInsert: true }\n );\n }\n } while (nextPageToken);\n },\n\n search: async (query, owner, accountId) => {\n const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n // Google Search Query\n // name contains 'query'\n const res = await drive.files.list({\n q: `name contains '${query}' and trashed = false`,\n fields: 'files(id, name, mimeType, size, parents, webViewLink, iconLink, thumbnailLink)',\n pageSize: 50,\n });\n\n const files = res.data.files || [];\n\n // Upsert results\n // Note: We might not know the local parentId for these items if we haven't synced their parents.\n // This makes it tricky. If we just upsert with parentId=null (root), they show up in root, which is confusing.\n // Option: Don't change parentId if exists. If new, maybe leave parentId null/undefined but we need it.\n // Strategy: Just update existing items metadata? \n // OR: \"Search\" just returns items without persisting parent structure?\n // But our UI relies on DB.\n // If we upsert new items from search, we must assign a parent.\n // Typically we can't assign accurate local parent ID without traversing up.\n // Compromise: Update properties of existing items. Ignore new items?\n // OR: Just let `list` handle creation. Search only works on things we've synced?\n // NO, user wants to find remote files.\n // Allow creating with parentId = null (root) for now? Or keep them \"orphaned\" (parentId: undefined) if that works?\n // Drive schema requires parentId? default null.\n // Let's skip upserting NEW items from search for now to avoid mess. \n // Only update existing. \n // Wait, if I search for \"Report\", I want to see \"Report.pdf\" even if I never opened the folder.\n // This implies I need to sync it.\n // For now, I will NOT upsert in search to keep safety. search only funds synced items.\n // Re-evaluating: The prompt said \"Search... mapping user queries to Google's q syntax\".\n // This implies meaningful results.\n // Let's implement basics: Sync results to DB. Parent = null (Root) if unknown.\n // Users can then \"Go to location\" -> might fail if path unknown.\n // Just upserting with `parentId: null` puts them in root.\n // I will omit parentId in update, so it keeps existing. If new, it defaults to null (Root). \n // This is acceptable behavior for \"All files\" view or similar.\n\n for (const file of files) {\n if (!file.id || !file.name) continue;\n const isFolder = file.mimeType === 'application/vnd.google-apps.folder';\n if (!isFolder && file.mimeType?.startsWith('application/vnd.google-apps.')) continue;\n\n const sizeInBytes = file.size ? parseInt(file.size) : 0;\n\n await Drive.findOneAndUpdate(\n { owner, 'provider.google.id': file.id, 'metadata.type': 'GOOGLE' },\n {\n $set: {\n name: file.name,\n storageAccountId: foundAccountId,\n information: {\n type: isFolder ? 'FOLDER' : 'FILE',\n sizeInBytes,\n mime: file.mimeType,\n path: ''\n },\n metadata: {\n type: 'GOOGLE'\n },\n provider: {\n google: {\n id: file.id,\n webViewLink: file.webViewLink,\n iconLink: file.iconLink,\n thumbnailLink: file.thumbnailLink\n }\n },\n // Don't overwrite parentId if it exists. \n // New items will default to null (Root) via schema default\n }\n },\n { upsert: true, setDefaultsOnInsert: true }\n );\n }\n },\n\n getQuota: async (owner, accountId) => {\n try {\n const { client } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n const res: any = await drive.about.get({ fields: 'storageQuota' });\n return {\n usedInBytes: parseInt(res.data.storageQuota?.usage || '0'),\n quotaInBytes: parseInt(res.data.storageQuota?.limit || '0')\n };\n } catch {\n return { usedInBytes: 0, quotaInBytes: 0 };\n }\n },\n\n openStream: async (item: IDatabaseDriveDocument, accountId?: string) => {\n const { client } = await createAuthClient(item.owner, accountId || (item.storageAccountId?.toString()));\n const drive = google.drive({ version: 'v3', auth: client });\n\n if (!item.provider?.google?.id) throw new Error('Missing Google File ID');\n\n // Check if we can stream functionality\n if (item.information.type === 'FOLDER') throw new Error('Cannot stream folder');\n\n const res: any = await drive.files.get(\n { fileId: item.provider.google.id, alt: 'media' },\n { responseType: 'stream' }\n );\n\n return {\n stream: res.data as Readable,\n mime: item.information.mime,\n size: item.information.sizeInBytes\n };\n },\n\n getThumbnail: async (item: IDatabaseDriveDocument, accountId?: string) => {\n const { client } = await createAuthClient(item.owner, accountId || (item.storageAccountId?.toString()));\n\n if (!item.provider?.google?.thumbnailLink) throw new Error('No thumbnail available');\n\n const res: any = await client.request({ url: item.provider.google.thumbnailLink, responseType: 'stream' });\n return res.data as Readable;\n },\n\n createFolder: async (name, parentId, owner, accountId) => {\n const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n let googleParentId = 'root';\n if (parentId && parentId !== 'root') {\n const parent = await Drive.findOne({ _id: parentId, owner });\n if (parent?.provider?.google?.id) googleParentId = parent.provider.google.id;\n }\n\n const res: any = await drive.files.create({\n requestBody: {\n name,\n mimeType: 'application/vnd.google-apps.folder',\n parents: [googleParentId]\n },\n fields: 'id, name, mimeType, webViewLink, iconLink'\n });\n\n const file = res.data;\n if (!file.id) throw new Error('Failed to create folder on Google Drive');\n\n // Create local record\n const folder = new Drive({\n owner,\n name: file.name,\n parentId: parentId === 'root' || !parentId ? null : parentId,\n provider: {\n type: 'GOOGLE',\n google: {\n id: file.id,\n webViewLink: file.webViewLink,\n iconLink: file.iconLink\n }\n },\n storageAccountId: foundAccountId,\n information: { type: 'FOLDER' },\n status: 'READY'\n });\n await folder.save();\n return folder.toClient();\n },\n\n uploadFile: async (drive, filePath, accountId) => {\n if (drive.information.type !== 'FILE') throw new Error('Invalid drive type');\n\n const { client } = await createAuthClient(drive.owner, accountId || (drive.storageAccountId?.toString()));\n const googleDrive = google.drive({ version: 'v3', auth: client });\n\n let googleParentId = 'root';\n if (drive.parentId) {\n const parent = await Drive.findById(drive.parentId);\n if (parent?.provider?.google?.id) googleParentId = parent.provider.google.id;\n }\n\n try {\n const res: any = await googleDrive.files.create({\n requestBody: {\n name: drive.name,\n parents: [googleParentId],\n mimeType: drive.information.mime\n },\n media: {\n mimeType: drive.information.mime,\n body: fs.createReadStream(filePath)\n },\n fields: 'id, name, mimeType, webViewLink, iconLink, thumbnailLink, size'\n });\n\n const gFile = res.data;\n if (!gFile.id) throw new Error('Upload to Google Drive failed');\n\n // Update Drive record\n drive.status = 'READY';\n drive.provider = {\n type: 'GOOGLE',\n google: {\n id: gFile.id,\n webViewLink: gFile.webViewLink || undefined,\n iconLink: gFile.iconLink || undefined,\n thumbnailLink: gFile.thumbnailLink || undefined\n }\n };\n\n // Note: We don't delete the temp file here, index.ts handles cleanup\n\n } catch (error) {\n drive.status = 'FAILED';\n console.error('Google Upload Error:', error);\n throw error;\n }\n\n await drive.save();\n return drive.toClient();\n },\n\n delete: async (ids, owner, accountId) => {\n const { client } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n const items = await Drive.find({ _id: { $in: ids }, owner });\n\n for (const item of items) {\n if (item.provider?.google?.id) {\n try {\n await drive.files.delete({ fileId: item.provider.google.id });\n } catch (e) {\n console.error('Failed to delete Google file', e);\n }\n }\n }\n\n await Drive.deleteMany({ _id: { $in: ids } });\n },\n\n trash: async (ids, owner, accountId) => {\n const { client } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n const items = await Drive.find({ _id: { $in: ids }, owner });\n\n for (const item of items) {\n if (item.provider?.google?.id) {\n try {\n await drive.files.update({\n fileId: item.provider.google.id,\n requestBody: { trashed: true }\n });\n } catch (e) {\n console.error('Failed to trash Google file', e);\n }\n }\n }\n },\n\n untrash: async (ids, owner, accountId) => {\n const { client } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n const items = await Drive.find({ _id: { $in: ids }, owner });\n\n for (const item of items) {\n if (item.provider?.google?.id) {\n try {\n await drive.files.update({\n fileId: item.provider.google.id,\n requestBody: { trashed: false }\n });\n } catch (e) {\n console.error('Failed to restore Google file', e);\n }\n }\n }\n },\n\n rename: async (id, newName, owner, accountId) => {\n const { client } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n const item = await Drive.findOne({ _id: id, owner });\n if (!item || !item.provider?.google?.id) throw new Error('Item not found');\n\n await drive.files.update({\n fileId: item.provider.google.id,\n requestBody: { name: newName }\n });\n\n item.name = newName;\n await item.save();\n return item.toClient();\n },\n\n move: async (id, newParentId, owner, accountId) => {\n const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);\n const drive = google.drive({ version: 'v3', auth: client });\n\n // Get Item\n const item = await Drive.findOne({ _id: id, owner });\n if (!item || !item.provider?.google?.id) throw new Error('Item not found or not synced');\n\n // Resolve Old Parent Google ID\n let previousGoogleParentId: string | undefined = undefined;\n if (item.parentId) {\n const oldParent = await Drive.findOne({ _id: item.parentId, owner });\n if (oldParent && oldParent.provider?.google?.id) {\n previousGoogleParentId = oldParent.provider.google.id;\n }\n } else {\n // If parentId is null (root), finding previous parent is tricky without querying Google\n // But usually we don't need to specify removeParents if we don't care about multi-parenting?\n // Actually, in Drive API v3, moving requires removeParents if you want to 'move' and not 'add to'.\n // Let's query parents from Google to be safe\n try {\n const gFile = await drive.files.get({ fileId: item.provider.google.id, fields: 'parents' });\n if (gFile.data.parents && gFile.data.parents.length > 0) {\n previousGoogleParentId = gFile.data.parents.join(',');\n }\n } catch (e) {\n console.warn('Could not fetch parents for move', e);\n }\n }\n\n // Resolve New Parent Google ID\n let newGoogleParentId = 'root'; // User's root (maybe specific root folder if we support that?)\n // For now, assume 'root' maps to Drive root (or myDrive)\n if (newParentId && newParentId !== 'root') {\n const newParent = await Drive.findOne({ _id: newParentId, owner });\n if (!newParent || !newParent.provider?.google?.id) throw new Error('Target folder not found in Google Drive');\n newGoogleParentId = newParent.provider.google.id;\n }\n\n // Call Google API\n await drive.files.update({\n fileId: item.provider.google.id,\n addParents: newGoogleParentId,\n removeParents: previousGoogleParentId,\n fields: 'id, parents'\n });\n\n // Update DB\n item.parentId = newParentId === 'root' || !newParentId ? null : new mongoose.Types.ObjectId(newParentId);\n await item.save();\n\n return item.toClient();\n },\n\n revokeToken: async (owner, accountId) => {\n if (!accountId) return; // Need specific account to revoke\n const { client } = await createAuthClient(owner, accountId);\n const account = await StorageAccount.findById(accountId);\n if (account?.metadata?.provider === 'GOOGLE' && account.metadata.google?.credentials) {\n const creds = account.metadata.google.credentials;\n if (typeof creds === 'object' && 'access_token' in creds) {\n await client.revokeToken(creds.access_token);\n }\n }\n }\n};\n","import fs from 'fs';\nimport path from 'path';\nimport crypto from 'crypto';\nimport formidable from 'formidable';\nimport type { Readable } from 'stream';\nimport Drive from '@/server/database/mongoose/schema/drive';\nimport { getDriveConfig } from '@/server/config';\nimport { computeFileHash, extractImageMetadata } from '@/server/utils';\nimport type { IDatabaseDriveDocument } from '@/server/database/mongoose/schema/drive';\nimport type { TDatabaseDrive } from '@/types/lib/database/drive';\nimport { LocalStorageProvider } from '@/server/providers/local';\nimport { GoogleDriveProvider } from '@/server/providers/google';\n\nexport const getNextOrderValue = async (owner: Record<string, unknown> | null): Promise<number> => {\n const lastItem = await Drive.findOne({ owner }, {}, { sort: { order: -1 } });\n return lastItem ? lastItem.order + 1 : 0;\n};\n\nexport const getStorageUsed = async (owner: Record<string, unknown> | null): Promise<number> => {\n try {\n const result = await Drive.aggregate([{ $match: { owner, 'information.type': 'FILE', trashedAt: null } }, { $group: { _id: null, total: { $sum: '$information.sizeInBytes' } } }]);\n return result[0]?.total || 0;\n } catch {\n return 0;\n }\n};\n\nexport const getAllFolderContents = async (folderId: string, owner: Record<string, unknown> | null, maxDepth = 50, currentDepth = 0): Promise<IDatabaseDriveDocument[]> => {\n if (currentDepth >= maxDepth) throw new Error('Maximum folder depth exceeded');\n\n const items = await Drive.find({ parentId: folderId, owner }).exec();\n const result: IDatabaseDriveDocument[] = [...items];\n\n for (const item of items) {\n if (item.information.type === 'FOLDER') {\n const subItems = await getAllFolderContents(String(item._id), owner, maxDepth, currentDepth + 1);\n result.push(...subItems);\n }\n }\n return result;\n};\n\nexport const driveGetUrl = (fileId: string, options?: { expiry?: number | Date }): string => {\n const config = getDriveConfig();\n if (!config.security.signedUrls?.enabled) {\n return `/api/drive?action=serve&id=${fileId}`;\n }\n\n const { secret, expiresIn } = config.security.signedUrls;\n let expiryTimestamp: number;\n\n if (options?.expiry instanceof Date) {\n expiryTimestamp = Math.floor(options.expiry.getTime() / 1000);\n } else if (typeof options?.expiry === 'number') {\n expiryTimestamp = Math.floor(Date.now() / 1000) + options.expiry;\n } else {\n expiryTimestamp = Math.floor(Date.now() / 1000) + expiresIn;\n }\n\n const signature = crypto.createHmac('sha256', secret).update(`${fileId}:${expiryTimestamp}`).digest('hex');\n const token = Buffer.from(`${expiryTimestamp}:${signature}`).toString('base64url');\n return `/api/drive?action=serve&id=${fileId}&token=${token}`;\n};\n\n/**\n * Read a file and return a readable stream.\n * @param file - Either a file ID (string) or a TDatabaseDrive/IDatabaseDriveDocument object\n * @returns Promise with readable stream, mime type, and file size\n * @example\n * ```typescript\n * // Using file ID\n * const { stream, mime, size } = await driveReadFile('694f5013226de007be94fcc0');\n * stream.pipe(response);\n * \n * // Using database document\n * const drive = await Drive.findById(fileId);\n * const { stream, mime, size } = await driveReadFile(drive);\n * ```\n */\nexport const driveReadFile = async (\n file: string | IDatabaseDriveDocument | TDatabaseDrive\n): Promise<{ stream: Readable; mime: string; size: number }> => {\n let drive: IDatabaseDriveDocument;\n\n // If file is a string (ID), fetch from database\n if (typeof file === 'string') {\n const doc = await Drive.findById(file);\n if (!doc) throw new Error(`File not found: ${file}`);\n drive = doc;\n } else if ('toClient' in file) {\n // Already an IDatabaseDriveDocument (Mongoose document)\n drive = file;\n } else {\n throw new Error('Invalid file parameter provided');\n }\n\n if (drive.information.type !== 'FILE') {\n throw new Error('Cannot read a folder');\n }\n\n // Determine provider based on drive's provider type\n const provider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const accountId = drive.storageAccountId?.toString();\n\n // Use provider's openStream method\n return await provider.openStream(drive, accountId);\n};\n\n/**\n * Get the local file system path for a file. For Google Drive files, downloads them to a local cache first.\n * @param file - Either a file ID (string) or a TDatabaseDrive/IDatabaseDriveDocument object\n * @returns Promise with readonly object containing the absolute file path and metadata\n * @example\n * ```typescript\n * // Using file ID\n * const { path, mime, size } = await driveFilePath('694f5013226de007be94fcc0');\n * const data = fs.readFileSync(path);\n * \n * // Using database document\n * const drive = await Drive.findById(fileId);\n * const { path, name, provider } = await driveFilePath(drive);\n * await processFile(path);\n * ```\n */\nexport const driveFilePath = async (\n file: string | IDatabaseDriveDocument | TDatabaseDrive\n): Promise<Readonly<{ path: string; name: string; mime: string; size: number; provider: string }>> => {\n let drive: IDatabaseDriveDocument;\n\n // If file is a string (ID), fetch from database\n if (typeof file === 'string') {\n const doc = await Drive.findById(file);\n if (!doc) throw new Error(`File not found: ${file}`);\n drive = doc;\n } else if ('toClient' in file) {\n // Already an IDatabaseDriveDocument (Mongoose document)\n drive = file;\n } else {\n throw new Error('Invalid file parameter provided');\n }\n\n if (drive.information.type !== 'FILE') {\n throw new Error('Cannot get path for a folder');\n }\n\n const config = getDriveConfig();\n const STORAGE_PATH = config.storage.path;\n const providerType = drive.provider?.type || 'LOCAL';\n\n // For LOCAL files, return the existing path directly\n if (providerType === 'LOCAL') {\n const filePath = path.join(STORAGE_PATH, drive.information.path);\n\n if (!fs.existsSync(filePath)) {\n throw new Error(`Local file not found on disk: ${filePath}`);\n }\n\n return Object.freeze({\n path: filePath,\n name: drive.name,\n mime: drive.information.mime,\n size: drive.information.sizeInBytes,\n provider: 'LOCAL'\n });\n }\n\n // For GOOGLE files, download to cache library folder\n if (providerType === 'GOOGLE') {\n const libraryDir = path.join(STORAGE_PATH, 'library', 'google');\n const fileName = `${drive._id}${path.extname(drive.name)}`;\n const cachedFilePath = path.join(libraryDir, fileName);\n\n // Check if already cached\n if (fs.existsSync(cachedFilePath)) {\n const stats = fs.statSync(cachedFilePath);\n\n // Verify cached file size matches (simple integrity check)\n if (stats.size === drive.information.sizeInBytes) {\n return Object.freeze({\n path: cachedFilePath,\n name: drive.name,\n mime: drive.information.mime,\n size: drive.information.sizeInBytes,\n provider: 'GOOGLE'\n });\n }\n\n // Size mismatch, re-download\n fs.unlinkSync(cachedFilePath);\n }\n\n // Download from Google Drive\n const accountId = drive.storageAccountId?.toString();\n const { stream } = await GoogleDriveProvider.openStream(drive, accountId);\n\n // Create library directory if it doesn't exist\n if (!fs.existsSync(libraryDir)) {\n fs.mkdirSync(libraryDir, { recursive: true });\n }\n\n // Download to temp file first, then rename (atomic operation)\n const tempPath = `${cachedFilePath}.tmp`;\n const writeStream = fs.createWriteStream(tempPath);\n\n await new Promise<void>((resolve, reject) => {\n stream.pipe(writeStream);\n writeStream.on('finish', resolve);\n writeStream.on('error', reject);\n stream.on('error', reject);\n });\n\n // Rename temp file to final path\n fs.renameSync(tempPath, cachedFilePath);\n\n return Object.freeze({\n path: cachedFilePath,\n name: drive.name,\n mime: drive.information.mime,\n size: drive.information.sizeInBytes,\n provider: 'GOOGLE'\n });\n }\n\n throw new Error(`Unsupported provider: ${providerType}`);\n};\n\nexport const processChunk = async (drive: any, chunkFile: formidable.File | formidable.File[] | undefined, chunkIndex: number, totalChunks: number, STORAGE_PATH: string) => {\n if (!chunkFile || drive.information.type !== 'FILE') return;\n\n const file = Array.isArray(chunkFile) ? chunkFile[0] : chunkFile;\n const destPath = path.join(STORAGE_PATH, drive.information.path);\n const chunkData = fs.readFileSync(file.filepath);\n\n if (chunkIndex === 0) {\n fs.writeFileSync(destPath, chunkData);\n } else {\n fs.appendFileSync(destPath, chunkData);\n }\n\n fs.rmSync(file.filepath, { force: true });\n drive.currentChunk = chunkIndex + 1;\n\n if (drive.currentChunk >= totalChunks || chunkIndex === totalChunks - 1) {\n drive.status = 'READY';\n drive.information.hash = await computeFileHash(destPath);\n\n if (drive.information.mime.startsWith('image/')) {\n const meta = await extractImageMetadata(destPath);\n if (meta) {\n drive.information.width = meta.width;\n drive.information.height = meta.height;\n }\n }\n }\n\n await drive.save();\n};\n"],"mappings":";;;;;;;;AAEA,OAAO,gBAAgB;AACvB,OAAOA,WAAU;AACjB,OAAOC,SAAQ;;;ACFf,OAAO,cAAc;AAGrB,IAAI,eAA2C;AAGxC,IAAM,qBAAqB,CAAC,WAAqD;AAEpF,MAAI,SAAS,WAAW,eAAe,GAAG;AACtC,UAAM,IAAI,MAAM,2FAA2F;AAAA,EAC/G;AAGA,QAAM,eAAoC;AAAA,IACtC,GAAG;AAAA,IACH,UAAU;AAAA,MACN,sBAAsB,OAAO,UAAU,wBAAwB,KAAK,OAAO;AAAA;AAAA,MAC3E,kBAAkB,OAAO,UAAU,oBAAoB,CAAC,KAAK;AAAA,MAC7D,YAAY,OAAO,UAAU;AAAA,MAC7B,OAAO,OAAO,UAAU;AAAA,IAC5B;AAAA,IACA,aAAa,OAAO,gBAAgB,OAAO,QAAQ;AAC/C,aAAO;AAAA,QACH,KAAK,EAAE,IAAI,eAAe;AAAA,QAC1B,SAAS,EAAE,cAAc,KAAK,OAAO,OAAO,KAAK;AAAA;AAAA,MACrD;AAAA,IACJ;AAAA,EACJ;AAEA,iBAAe;AACf,SAAO;AACX;AAGO,IAAM,iBAAiB,MAA2B;AACrD,MAAI,CAAC,aAAc,OAAM,IAAI,MAAM,qCAAqC;AACxE,SAAO;AACX;AAGO,IAAM,sBAAsB,OAAO,QAA0D;AAChG,QAAM,SAAS,eAAe;AAC9B,SAAO,OAAO,YAAY,GAAG;AACjC;;;ACrCA,OAAOC,aAAY,cAAqD;AAmBxE,IAAM,oBAAoB,IAAI,OAAO;AAAA,EACjC,MAAM,EAAE,MAAM,QAAQ,MAAM,CAAC,QAAQ,QAAQ,GAAG,UAAU,KAAK;AAAA,EAC/D,aAAa,EAAE,MAAM,QAAQ,SAAS,EAAE;AAAA,EACxC,MAAM,EAAE,MAAM,OAAO;AAAA,EACrB,MAAM,EAAE,MAAM,OAAO;AAAA,EACrB,OAAO,EAAE,MAAM,OAAO;AAAA,EACtB,QAAQ,EAAE,MAAM,OAAO;AAAA,EACvB,UAAU,EAAE,MAAM,OAAO;AAAA,EACzB,MAAM,EAAE,MAAM,OAAO;AACzB,GAAG,EAAE,KAAK,MAAM,CAAC;AAGjB,IAAM,iBAAiB,IAAI,OAAO;AAAA,EAC9B,MAAM,EAAE,MAAM,QAAQ,MAAM,CAAC,SAAS,QAAQ,GAAG,UAAU,MAAM,SAAS,QAAQ;AAAA,EAClF,QAAQ,EAAE,MAAM,OAAO,MAAM,MAAM;AACvC,GAAG,EAAE,KAAK,MAAM,CAAC;AAEjB,IAAM,cAAsB,IAAI;AAAA,EAC5B;AAAA,IACI,OAAO,EAAE,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK;AAAA,IACjD,kBAAkB,EAAE,MAAM,OAAO,MAAM,UAAU,KAAK,kBAAkB,SAAS,KAAK;AAAA,IACtF,MAAM,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACrC,UAAU,EAAE,MAAM,OAAO,MAAM,UAAU,KAAK,SAAS,SAAS,KAAK;AAAA,IACrE,OAAO,EAAE,MAAM,QAAQ,SAAS,EAAE;AAAA,IAClC,UAAU,EAAE,MAAM,gBAAgB,SAAS,OAAO,EAAE,MAAM,QAAQ,GAAG;AAAA,IACrE,UAAU,EAAE,MAAM,OAAO,MAAM,OAAO,SAAS,CAAC,EAAE;AAAA,IAClD,aAAa,EAAE,MAAM,mBAAmB,UAAU,KAAK;AAAA,IACvD,QAAQ,EAAE,MAAM,QAAQ,MAAM,CAAC,SAAS,cAAc,aAAa,QAAQ,GAAG,SAAS,aAAa;AAAA,IACpG,WAAW,EAAE,MAAM,MAAM,SAAS,KAAK;AAAA,IACvC,WAAW,EAAE,MAAM,MAAM,SAAS,KAAK,IAAI;AAAA,EAC/C;AAAA,EACA,EAAE,UAAU,MAAM;AACtB;AAGA,YAAY,MAAM,EAAE,OAAO,GAAG,oBAAoB,EAAE,CAAC;AACrD,YAAY,MAAM,EAAE,OAAO,GAAG,iBAAiB,GAAG,sBAAsB,EAAE,CAAC;AAC3E,YAAY,MAAM,EAAE,OAAO,GAAG,kBAAkB,EAAE,CAAC;AACnD,YAAY,MAAM,EAAE,OAAO,GAAG,WAAW,EAAE,CAAC;AAC5C,YAAY,MAAM,EAAE,OAAO,GAAG,oBAAoB,EAAE,CAAC;AACrD,YAAY,MAAM,EAAE,OAAO,GAAG,MAAM,OAAO,CAAC;AAC5C,YAAY,MAAM,EAAE,OAAO,GAAG,iBAAiB,EAAE,CAAC;AAGlD,YAAY,OAA+B,YAAY,iBAA2C;AAC9F,QAAM,OAAO,KAAK,OAA+B;AAEjD,SAAO;AAAA,IACH,IAAI,OAAO,KAAK,GAAG;AAAA,IACnB,MAAM,KAAK;AAAA,IACX,UAAU,KAAK,WAAW,OAAO,KAAK,QAAQ,IAAI;AAAA,IAClD,OAAO,KAAK;AAAA,IACZ,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,aAAa,KAAK;AAAA,IAClB,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK;AAAA,EACpB;AACJ,CAAC;AAED,IAAM,QAAuCA,UAAS,OAAO,SAASA,UAAS,MAA8B,SAAS,WAAW;AAEjI,IAAO,gBAAQ;;;ACxFf,OAAOC,aAAY,UAAAC,eAAqD;AAYxE,IAAM,uBAA+B,IAAIA;AAAA,EACrC;AAAA,IACI,OAAO,EAAE,MAAMA,QAAO,MAAM,OAAO,SAAS,KAAK;AAAA,IACjD,MAAM,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACrC,UAAU;AAAA,MACN,UAAU,EAAE,MAAM,QAAQ,MAAM,CAAC,QAAQ,GAAG,UAAU,KAAK;AAAA,MAC3D,QAAQ;AAAA,QACJ,OAAO,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,QACtC,aAAa,EAAE,MAAMA,QAAO,MAAM,OAAO,UAAU,KAAK;AAAA,MAC5D;AAAA,IACJ;AAAA,IACA,WAAW,EAAE,MAAM,MAAM,SAAS,KAAK,IAAI;AAAA,EAC/C;AAAA,EACA,EAAE,UAAU,MAAM;AACtB;AAGA,qBAAqB,MAAM,EAAE,OAAO,GAAG,qBAAqB,EAAE,CAAC;AAC/D,qBAAqB,MAAM,EAAE,OAAO,GAAG,yBAAyB,EAAE,CAAC;AAGnE,qBAAqB,OAAwC,YAAY,iBAAoD;AACzH,QAAM,OAAO,KAAK,OAAwC;AAE1D,SAAO;AAAA,IACH,IAAI,OAAO,KAAK,GAAG;AAAA,IACnB,OAAO,KAAK;AAAA,IACZ,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,IACf,WAAW,KAAK;AAAA,EACpB;AACJ,CAAC;AAED,IAAM,iBAAyDD,UAAS,OAAO,kBAAkBA,UAAS,MAAuC,kBAAkB,oBAAoB;AAEvL,IAAO,kBAAQ;;;ACjDf,OAAO,QAAQ;AACf,OAAOE,aAAY;AACnB,OAAO,WAAW;AAYX,IAAM,mBAAmB,CAAC,MAAc,iBAAoC;AAClF,MAAI,aAAa,SAAS,KAAK,EAAG,QAAO;AACzC,SAAO,aAAa,KAAK,aAAW;AACnC,QAAI,YAAY,KAAM,QAAO;AAC7B,QAAI,QAAQ,SAAS,IAAI,GAAG;AAC3B,YAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,aAAO,KAAK,WAAW,GAAG,MAAM,GAAG;AAAA,IACpC;AACA,WAAO;AAAA,EACR,CAAC;AACF;AAEO,IAAM,kBAAkB,CAAC,aAC/B,IAAI,QAAQ,CAAC,SAAS,WAAW;AAChC,QAAM,OAAOC,QAAO,WAAW,QAAQ;AACvC,QAAM,SAAS,GAAG,iBAAiB,QAAQ;AAC3C,SAAO,GAAG,QAAQ,UAAQ,KAAK,OAAO,IAAI,CAAC;AAC3C,SAAO,GAAG,OAAO,MAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,CAAC;AAClD,SAAO,GAAG,SAAS,MAAM;AAC1B,CAAC;AAEK,IAAM,uBAAuB,OAAO,aAAqB;AAC/D,MAAI;AACH,UAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,MAAM,QAAQ,EAAE,SAAS;AACvE,WAAO,EAAE,OAAO,QAAQ,GAAI,QAAQ,EAAE,MAAM,EAAE,KAAK,KAAK,SAAS,QAAQ,EAAE,EAAE,EAAG;AAAA,EACjF,QAAQ;AACP,WAAO;AAAA,EACR;AACD;;;AC1CA,SAAS,SAAS;AAClB,SAAS,uBAAuB;AAGhC,IAAM,iBAAiB,EAAE,OAAO,EAAE,OAAO,SAAO,gBAAgB,GAAG,GAAG;AAAA,EAClE,SAAS;AACb,CAAC;AAGD,IAAM,mBAAmB,CAAC,SAAyB;AAC/C,SACI,KACK,QAAQ,uBAAuB,EAAE,EACjC,QAAQ,QAAQ,EAAE,EAClB,QAAQ,QAAQ,EAAE,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,QAAQ,GAAG,EACnB,QAAQ,WAAW,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,MAAM,GAAG,EACT,IAAI,KACT,GACK,KAAK,EACL,MAAM,GAAG,GAAG;AAEzB;AAGA,IAAM,qBAAqB,CAAC,UAA0B;AAElD,SAAO,MAAM,QAAQ,uBAAuB,MAAM,EAAE,MAAM,GAAG,GAAG;AACpE;AAGA,IAAM,aAAa,EACd,OAAO,EACP,IAAI,GAAG,kBAAkB,EACzB,IAAI,KAAK,eAAe,EACxB,UAAU,gBAAgB,EAC1B,OAAO,SAAO,IAAI,SAAS,GAAG,EAAE,SAAS,kCAAkC,CAAC;AAG1E,IAAM,oBAAoB,EAC5B,OAAO;AAAA,EACJ,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAK;AAAA,EAC7C,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAK;AAAA,EAC9C,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,UAAU;AAAA,EACV,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,OAAO,gBAAgB;AAAA,EAC7D,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EACnC,UAAU,EAAE,OAAO,EAAE,SAAS;AAClC,CAAC,EACA,OAAO,UAAQ,KAAK,aAAa,KAAK,aAAa;AAAA,EAChD,SAAS;AACb,CAAC;AAGE,IAAM,kBAAkB,EAAE,OAAO;AAAA,EACpC,UAAU,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,gBAAgB,EAAE,UAAU,CAAC,CAAC;AAAA,EACpE,OAAO,EACF,OAAO,EACP,SAAS,EACT,UAAU,SAAO;AACd,UAAM,MAAM,SAAS,OAAO,MAAM,EAAE;AACpC,WAAO,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,GAAG,GAAG;AAAA,EACzC,CAAC;AAAA,EACL,SAAS,eAAe,SAAS;AACrC,CAAC;AAGM,IAAM,mBAAmB,EAAE,OAAO;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,GAAG,EAAE,KAAK,CAAC,YAAY,OAAO,UAAU,QAAQ,QAAQ,CAAC,EAAE,SAAS;AAAA,EACpE,QAAQ,EAAE,KAAK,CAAC,QAAQ,QAAQ,KAAK,CAAC,EAAE,SAAS;AACrD,CAAC;AAGM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EACzC,IAAI;AAAA,EACJ,MAAM,EAAE,KAAK,CAAC,SAAS,UAAU,OAAO,CAAC,EAAE,SAAS,EAAE,QAAQ,QAAQ;AAAA,EACtE,OAAO,EAAE,OAAO,EAAE,SAAS;AAC/B,CAAC;AAGM,IAAM,mBAAmB,EAAE,OAAO;AAAA,EACrC,IAAI;AAAA,EACJ,SAAS;AACb,CAAC;AAGM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACtC,IAAI;AACR,CAAC;AAGM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EACzC,KAAK,EAAE,MAAM,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAChD,CAAC;AAGM,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC3C,MAAM;AAAA,EACN,UAAU,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,gBAAgB,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS;AACzG,CAAC;AAGM,IAAM,iBAAiB,EAAE,OAAO;AAAA,EACnC,KAAK,EAAE,MAAM,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EAC5C,gBAAgB,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,gBAAgB,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS;AACzF,CAAC;AAGM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACtC,KAAK,EAAE,MAAM,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAChD,CAAC;AAGM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACtC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,UAAU,kBAAkB;AAAA,EAC1D,UAAU,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,gBAAgB,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS;AAAA,EAC/E,OAAO,EACF,OAAO,EACP,SAAS,EACT,UAAU,SAAO;AACd,UAAM,MAAM,SAAS,OAAO,MAAM,EAAE;AACpC,WAAO,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,GAAG,GAAG;AAAA,EACzC,CAAC;AAAA,EACL,SAAS,EACJ,OAAO,EACP,SAAS,EACT,UAAU,SAAO,QAAQ,MAAM;AACxC,CAAC;AAGM,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACvC,IAAI;AACR,CAAC;AAGM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACtC,IAAI;AACR,CAAC;AAGM,IAAM,wBAAwB,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AACpD,CAAC;AAGM,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACvC,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,IACX,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO;AAAA,EACnB,CAAC;AACL,CAAC;;;AC1GM,SAAS,mCAAmC,UAA0B;AAE5E,QAAM,WAAW,SAAS,QAAQ,aAAa,EAAE;AAGjD,SAAO,SACL,QAAQ,YAAY,EAAE,EACtB,QAAQ,iBAAiB,EAAE,EAC3B,MAAM,GAAG,GAAG;AACf;;;AC5DA,OAAOC,SAAQ;AACf,OAAO,UAAU;AACjB,OAAOC,eAAc;AAMrB,OAAO,YAAY;AAEnB,IAAM,eAAe,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS;AAEhD,IAAM,uBAAyC;AAAA,EAClD,MAAM;AAAA,EAEN,MAAM,OAAO,UAAU,OAAO,cAAc;AAAA,EAE5C;AAAA,EAEA,QAAQ,OAAO,OAAO,OAAO,cAAc;AAAA,EAE3C;AAAA,EAEA,UAAU,OAAO,OAAO,cAAc;AAElC,UAAM,SAAS,MAAM,cAAM,UAAU;AAAA,MACjC,EAAE,QAAQ,EAAE,OAAO,oBAAoB,QAAQ,WAAW,KAAK,EAAE;AAAA,MACjE,EAAE,QAAQ,EAAE,KAAK,MAAM,OAAO,EAAE,MAAM,2BAA2B,EAAE,EAAE;AAAA,IACzE,CAAC;AACD,UAAM,cAAc,OAAO,CAAC,GAAG,SAAS;AAMxC,UAAM,SAAS,eAAe;AAE9B,WAAO,EAAE,aAAa,cAAc,KAAK,OAAO,OAAO,KAAK;AAAA,EAChE;AAAA,EAEA,YAAY,OAAO,MAA8B,cAAuB;AACpE,QAAI,KAAK,YAAY,SAAS,OAAQ,OAAM,IAAI,MAAM,sBAAsB;AAC5E,UAAM,WAAW,KAAK,KAAK,cAAc,KAAK,YAAY,IAAI;AAE9D,QAAI,CAACC,IAAG,WAAW,QAAQ,GAAG;AAC1B,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC5C;AAEA,UAAM,OAAOA,IAAG,SAAS,QAAQ;AACjC,UAAM,SAASA,IAAG,iBAAiB,QAAQ;AAE3C,WAAO;AAAA,MACH;AAAA,MACA,MAAM,KAAK,YAAY;AAAA,MACvB,MAAM,KAAK;AAAA,IACf;AAAA,EACJ;AAAA,EAEA,cAAc,OAAO,MAA8B,cAAuB;AACtE,QAAI,KAAK,YAAY,SAAS,OAAQ,OAAM,IAAI,MAAM,yBAAyB;AAE/E,UAAM,eAAe,KAAK,KAAK,cAAc,KAAK,YAAY,IAAI;AAClE,UAAM,YAAY,KAAK,KAAK,cAAc,SAAS,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,OAAO;AAE9F,QAAI,CAACA,IAAG,WAAW,YAAY,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAE3E,QAAIA,IAAG,WAAW,SAAS,GAAG;AAC1B,aAAOA,IAAG,iBAAiB,SAAS;AAAA,IACxC;AAGA,QAAI,CAACA,IAAG,WAAW,KAAK,QAAQ,SAAS,CAAC,EAAG,CAAAA,IAAG,UAAU,KAAK,QAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAEtG,QAAI,KAAK,YAAY,KAAK,WAAW,QAAQ,GAAG;AAC5C,YAAMC,SAAQ,UAAQ,OAAO;AAC7B,YAAMA,OAAM,YAAY,EACnB,OAAO,KAAK,KAAK,EAAE,KAAK,SAAS,CAAC,EAClC,SAAS,QAAQ,EAAE,SAAS,GAAG,CAAC,EAChC,OAAO,SAAS;AAAA,IACzB,WAAW,KAAK,YAAY,KAAK,WAAW,QAAQ,GAAG;AACnD,YAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AACnC,eAAO,YAAY,EACd,YAAY;AAAA,UACT,OAAO;AAAA,UACP,QAAQ,KAAK,QAAQ,SAAS;AAAA,UAC9B,UAAU,KAAK,SAAS,SAAS;AAAA,UACjC,MAAM;AAAA,QACV,CAAC,EACA,GAAG,OAAO,OAAO,EACjB,GAAG,SAAS,MAAM;AAAA,MAC3B,CAAC;AAAA,IACL,OAAO;AACH,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACzD;AAEA,WAAOD,IAAG,iBAAiB,SAAS;AAAA,EACxC;AAAA,EAEA,cAAc,OAAO,MAAM,UAAU,OAAO,cAAc;AAEtD,UAAM,oBAAoB,OAAOE,WAA2D;AACxF,YAAM,WAAW,MAAM,cAAM,QAAQ,EAAE,OAAAA,OAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAG,EAAE,CAAC;AAC3E,aAAO,WAAW,SAAS,QAAQ,IAAI;AAAA,IAC3C;AAEA,UAAM,SAAS,IAAI,cAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA,UAAU,aAAa,UAAU,CAAC,WAAW,OAAO;AAAA,MACpD,OAAO,MAAM,kBAAkB,KAAK;AAAA,MACpC,UAAU,EAAE,MAAM,QAAQ;AAAA,MAC1B,aAAa,EAAE,MAAM,SAAS;AAAA,MAC9B,QAAQ;AAAA,IACZ,CAAC;AACD,UAAM,OAAO,KAAK;AAClB,WAAO,OAAO,SAAS;AAAA,EAC3B;AAAA,EAEA,YAAY,OAAO,OAAO,UAAU,cAAc;AAC9C,QAAI,MAAM,YAAY,SAAS,OAAQ,OAAM,IAAI,MAAM,oBAAoB;AAG3E,UAAM,WAAW,KAAK,KAAK,cAAc,MAAM,YAAY,IAAI;AAC/D,UAAM,UAAU,KAAK,QAAQ,QAAQ;AACrC,QAAI,CAACF,IAAG,WAAW,OAAO,EAAG,CAAAA,IAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAGtE,IAAAA,IAAG,WAAW,UAAU,QAAQ;AAEhC,UAAM,SAAS;AACf,UAAM,YAAY,OAAO,MAAM,gBAAgB,QAAQ;AAEvD,QAAI,MAAM,YAAY,KAAK,WAAW,QAAQ,GAAG;AAC7C,YAAM,OAAO,MAAM,qBAAqB,QAAQ;AAChD,UAAI,MAAM;AACN,cAAM,YAAY,QAAQ,KAAK;AAC/B,cAAM,YAAY,SAAS,KAAK;AAAA,MACpC;AAAA,IACJ;AAEA,UAAM,MAAM,KAAK;AACjB,WAAO,MAAM,SAAS;AAAA,EAC1B;AAAA,EAEA,QAAQ,OAAO,KAAK,OAAO,cAAc;AACrC,UAAM,QAAQ,MAAM,cAAM,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,MAAM,CAAC,EAAE,KAAK;AAGlE,UAAM,iBAAiB,OAAOG,eAAwC;AAClE,YAAM,WAAW,MAAM,cAAM,KAAK,EAAE,UAAU,EAAE,KAAKA,WAAU,GAAG,MAAM,CAAC,EAAE,KAAK;AAChF,UAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAEnC,YAAM,eAAe,SAChB,OAAO,OAAK,EAAE,YAAY,SAAS,QAAQ,EAC3C,IAAI,OAAK,EAAE,IAAI,SAAS,CAAC;AAE9B,YAAM,cAAc,MAAM,eAAe,YAAY;AACrD,aAAO,CAAC,GAAG,UAAU,GAAG,WAAW;AAAA,IACvC;AAEA,UAAM,YAAY,MAAM,OAAO,OAAK,EAAE,YAAY,SAAS,QAAQ,EAAE,IAAI,OAAK,EAAE,IAAI,SAAS,CAAC;AAC9F,UAAM,cAAc,MAAM,eAAe,SAAS;AAClD,UAAM,mBAAmB,CAAC,GAAG,OAAO,GAAG,WAAW;AAGlD,eAAW,QAAQ,kBAAkB;AACjC,UAAI,KAAK,YAAY,SAAS,UAAU,KAAK,YAAY,MAAM;AAG3D,cAAM,WAAW,KAAK,KAAK,cAAc,KAAK,YAAY,IAAI;AAC9D,cAAM,UAAU,KAAK,QAAQ,QAAQ;AACrC,YAAIH,IAAG,WAAW,OAAO,GAAG;AACxB,UAAAA,IAAG,OAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,QACvD;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,cAAM,WAAW,EAAE,KAAK,EAAE,KAAK,iBAAiB,IAAI,OAAK,EAAE,GAAG,EAAE,EAAE,CAAC;AAAA,EAC7E;AAAA,EAEA,OAAO,OAAO,KAAK,OAAO,cAAc;AAAA,EAExC;AAAA,EAEA,WAAW,OAAO,OAAO,cAAc;AAAA,EAEvC;AAAA,EAEA,SAAS,OAAO,KAAK,OAAO,cAAc;AAAA,EAE1C;AAAA,EAEA,QAAQ,OAAO,IAAI,SAAS,OAAO,cAAc;AAC7C,UAAM,OAAO,MAAM,cAAM;AAAA,MACrB,EAAE,KAAK,IAAI,MAAM;AAAA,MACjB,EAAE,MAAM,QAAQ;AAAA,MAChB,EAAE,KAAK,KAAK;AAAA,IAChB;AACA,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB;AAC3C,WAAO,KAAK,SAAS;AAAA,EACzB;AAAA,EAEA,MAAM,OAAO,IAAI,aAAa,OAAO,cAAc;AAC/C,UAAM,OAAO,MAAM,cAAM,QAAQ,EAAE,KAAK,IAAI,MAAM,CAAC;AACnD,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB;AAG3C,UAAM,cAAc,KAAK;AACzB,SAAK,WAAW,gBAAgB,UAAU,CAAC,cAAc,OAAO,IAAII,UAAS,MAAM,SAAS,WAAW;AAkBvG,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK,SAAS;AAAA,EACzB;AAAA,EAEA,aAAa,OAAO,OAAO,cAAc;AAAA,EAEzC;AACJ;;;AC1OA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,cAAc;AAEvB,OAAOC,eAAc;AAQrB,IAAMC,gBAAeC,MAAK,KAAK,QAAQ,IAAI,GAAG,SAAS;AAEvD,IAAM,mBAAmB,OAAO,OAAuC,cAAuB;AAE1F,QAAM,QAAa,EAAE,OAAO,qBAAqB,SAAS;AAC1D,MAAI,UAAW,OAAM,MAAM;AAI3B,QAAM,UAAU,MAAM,gBAAe,QAAQ,KAAK;AAClD,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,oCAAoC;AAElE,QAAM,SAAS,eAAe;AAC9B,QAAM,EAAE,UAAU,cAAc,YAAY,IAAI,OAAO,SAAS,UAAU,CAAC;AAE3E,MAAI,CAAC,YAAY,CAAC,aAAc,OAAM,IAAI,MAAM,6CAA6C;AAE7F,QAAM,eAAe,IAAI,OAAO,KAAK,OAAO,UAAU,cAAc,WAAW;AAa/E,MAAI,QAAQ,SAAS,aAAa,YAAY,CAAC,QAAQ,SAAS,QAAQ;AACpE,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACrD;AAEA,eAAa,eAAe,QAAQ,SAAS,OAAO,WAAW;AAG/D,eAAa,GAAG,UAAU,OAAO,WAAW;AACxC,QAAI,OAAO,eAAe;AACtB,cAAQ,SAAS,OAAO,cAAc,EAAE,GAAG,QAAQ,SAAS,OAAO,aAAa,GAAG,OAAO;AAC1F,cAAQ,aAAa,UAAU;AAC/B,YAAM,QAAQ,KAAK;AAAA,IACvB;AAAA,EACJ,CAAC;AAED,SAAO,EAAE,QAAQ,cAAc,WAAW,QAAQ,IAAI;AAC1D;AAEO,IAAM,sBAAwC;AAAA,EACjD,MAAM;AAAA,EAEN,MAAM,OAAO,UAAU,OAAO,cAAc;AACxC,UAAM,EAAE,QAAQ,WAAW,eAAe,IAAI,MAAM,iBAAiB,OAAO,SAAS;AACrF,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAG1D,QAAI,iBAAiB;AACrB,QAAI,YAAY,aAAa,QAAQ;AACjC,YAAM,SAAS,MAAM,cAAM,QAAQ,EAAE,KAAK,UAAU,MAAM,CAAC;AAC3D,UAAI,UAAU,OAAO,UAAU,QAAQ,IAAI;AACvC,yBAAiB,OAAO,SAAS,OAAO;AAAA,MAC5C,OAAO;AACH;AAAA,MACJ;AAAA,IACJ;AAIA,QAAI,gBAAoC;AAExC,UAAM,qBAAqB,oBAAI,IAAY;AAE3C,OAAG;AACC,YAAM,MAAW,MAAM,MAAM,MAAM,KAAK;AAAA,QACpC,GAAG,IAAI,cAAc;AAAA,QACrB,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,WAAW;AAAA,MACf,CAAC;AAED,sBAAgB,IAAI,KAAK,iBAAiB;AAC1C,YAAM,QAAQ,IAAI,KAAK,SAAS,CAAC;AAGjC,iBAAW,QAAQ,OAAO;AACtB,YAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ,CAAC,KAAK,SAAU;AAG9C,2BAAmB,IAAI,KAAK,EAAE;AAG9B,cAAM,WAAW,KAAK,aAAa;AAGnC,cAAM,cAAc,KAAK,OAAO,SAAS,KAAK,IAAI,IAAI;AAGtD,cAAM,aAAa;AAAA,UACf,MAAM,KAAK;AAAA,UACX,kBAAkB;AAAA,UAClB,UAAU,aAAa,SAAS,OAAO;AAAA,UACvC,aAAa;AAAA,YACT,MAAM,WAAW,WAAW;AAAA,YAC5B;AAAA,YACA,MAAM,KAAK;AAAA,YACX,MAAM;AAAA,UACV;AAAA,UACA,UAAU;AAAA,YACN,MAAM;AAAA,YACN,QAAQ;AAAA,cACJ,IAAI,KAAK;AAAA,cACT,aAAa,KAAK;AAAA,cAClB,UAAU,KAAK;AAAA,cACf,eAAe,KAAK;AAAA,YACxB;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR,WAAW;AAAA,QACf;AAEA,cAAM,cAAM;AAAA,UACR;AAAA,YACI;AAAA,YACA,sBAAsB,KAAK;AAAA,YAC3B,iBAAiB;AAAA,UACrB;AAAA,UACA,EAAE,MAAM,WAAW;AAAA,UACnB,EAAE,QAAQ,MAAM,KAAK,MAAM,qBAAqB,KAAK;AAAA,QACzD;AAAA,MACJ;AAAA,IACJ,SAAS;AAGT,UAAM,UAAU,MAAM,cAAM,KAAK;AAAA,MAC7B;AAAA,MACA,kBAAkB;AAAA,MAClB,UAAU,aAAa,SAAS,OAAO;AAAA,MACvC,iBAAiB;AAAA,IACrB,CAAC;AAED,eAAW,QAAQ,SAAS;AAExB,UAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,mBAAmB,IAAI,KAAK,SAAS,OAAO,EAAE,GAAG;AAC/E,aAAK,YAAY,oBAAI,KAAK;AAC1B,cAAM,KAAK,KAAK;AAAA,MACpB;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,WAAW,OAAO,OAAO,cAAc;AACnC,UAAM,EAAE,QAAQ,WAAW,eAAe,IAAI,MAAM,iBAAiB,OAAO,SAAS;AACrF,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAG1D,QAAI,gBAAoC;AAExC,OAAG;AACC,YAAM,MAAW,MAAM,MAAM,MAAM,KAAK;AAAA,QACpC,GAAG;AAAA,QACH,QAAQ;AAAA,QACR,UAAU;AAAA;AAAA,QACV,WAAW;AAAA,MACf,CAAC;AAED,sBAAgB,IAAI,KAAK,iBAAiB;AAC1C,YAAM,QAAQ,IAAI,KAAK,SAAS,CAAC;AAEjC,iBAAW,QAAQ,OAAO;AACtB,YAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ,CAAC,KAAK,SAAU;AAG9C,cAAM,WAAW,KAAK,aAAa;AAEnC,cAAM,cAAc,KAAK,OAAO,SAAS,KAAK,IAAI,IAAI;AAEtD,cAAM,cAAM;AAAA,UACR,EAAE,OAAO,sBAAsB,KAAK,IAAI,iBAAiB,SAAS;AAAA,UAClE;AAAA,YACI,MAAM;AAAA,cACF,MAAM,KAAK;AAAA,cACX,kBAAkB;AAAA,cAClB,aAAa;AAAA,gBACT,MAAM,WAAW,WAAW;AAAA,gBAC5B;AAAA,gBACA,MAAM,KAAK;AAAA,gBACX,MAAM;AAAA,cACV;AAAA,cACA,UAAU;AAAA,gBACN,MAAM;AAAA,gBACN,QAAQ;AAAA,kBACJ,IAAI,KAAK;AAAA,kBACT,aAAa,KAAK;AAAA,kBAClB,UAAU,KAAK;AAAA,kBACf,eAAe,KAAK;AAAA,gBACxB;AAAA,cACJ;AAAA,cACA,WAAW,oBAAI,KAAK;AAAA,YACxB;AAAA,UACJ;AAAA,UACA,EAAE,QAAQ,MAAM,qBAAqB,KAAK;AAAA,QAC9C;AAAA,MACJ;AAAA,IACJ,SAAS;AAAA,EACb;AAAA,EAEA,QAAQ,OAAO,OAAO,OAAO,cAAc;AACvC,UAAM,EAAE,QAAQ,WAAW,eAAe,IAAI,MAAM,iBAAiB,OAAO,SAAS;AACrF,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAI1D,UAAM,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC/B,GAAG,kBAAkB,KAAK;AAAA,MAC1B,QAAQ;AAAA,MACR,UAAU;AAAA,IACd,CAAC;AAED,UAAM,QAAQ,IAAI,KAAK,SAAS,CAAC;AA6BjC,eAAW,QAAQ,OAAO;AACtB,UAAI,CAAC,KAAK,MAAM,CAAC,KAAK,KAAM;AAC5B,YAAM,WAAW,KAAK,aAAa;AACnC,UAAI,CAAC,YAAY,KAAK,UAAU,WAAW,8BAA8B,EAAG;AAE5E,YAAM,cAAc,KAAK,OAAO,SAAS,KAAK,IAAI,IAAI;AAEtD,YAAM,cAAM;AAAA,QACR,EAAE,OAAO,sBAAsB,KAAK,IAAI,iBAAiB,SAAS;AAAA,QAClE;AAAA,UACI,MAAM;AAAA,YACF,MAAM,KAAK;AAAA,YACX,kBAAkB;AAAA,YAClB,aAAa;AAAA,cACT,MAAM,WAAW,WAAW;AAAA,cAC5B;AAAA,cACA,MAAM,KAAK;AAAA,cACX,MAAM;AAAA,YACV;AAAA,YACA,UAAU;AAAA,cACN,MAAM;AAAA,YACV;AAAA,YACA,UAAU;AAAA,cACN,QAAQ;AAAA,gBACJ,IAAI,KAAK;AAAA,gBACT,aAAa,KAAK;AAAA,gBAClB,UAAU,KAAK;AAAA,gBACf,eAAe,KAAK;AAAA,cACxB;AAAA,YACJ;AAAA;AAAA;AAAA,UAGJ;AAAA,QACJ;AAAA,QACA,EAAE,QAAQ,MAAM,qBAAqB,KAAK;AAAA,MAC9C;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,UAAU,OAAO,OAAO,cAAc;AAClC,QAAI;AACA,YAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,OAAO,SAAS;AAC1D,YAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAC1D,YAAM,MAAW,MAAM,MAAM,MAAM,IAAI,EAAE,QAAQ,eAAe,CAAC;AACjE,aAAO;AAAA,QACH,aAAa,SAAS,IAAI,KAAK,cAAc,SAAS,GAAG;AAAA,QACzD,cAAc,SAAS,IAAI,KAAK,cAAc,SAAS,GAAG;AAAA,MAC9D;AAAA,IACJ,QAAQ;AACJ,aAAO,EAAE,aAAa,GAAG,cAAc,EAAE;AAAA,IAC7C;AAAA,EACJ;AAAA,EAEA,YAAY,OAAO,MAA8B,cAAuB;AACpE,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,KAAK,OAAO,aAAc,KAAK,kBAAkB,SAAS,CAAE;AACtG,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAE1D,QAAI,CAAC,KAAK,UAAU,QAAQ,GAAI,OAAM,IAAI,MAAM,wBAAwB;AAGxE,QAAI,KAAK,YAAY,SAAS,SAAU,OAAM,IAAI,MAAM,sBAAsB;AAE9E,UAAM,MAAW,MAAM,MAAM,MAAM;AAAA,MAC/B,EAAE,QAAQ,KAAK,SAAS,OAAO,IAAI,KAAK,QAAQ;AAAA,MAChD,EAAE,cAAc,SAAS;AAAA,IAC7B;AAEA,WAAO;AAAA,MACH,QAAQ,IAAI;AAAA,MACZ,MAAM,KAAK,YAAY;AAAA,MACvB,MAAM,KAAK,YAAY;AAAA,IAC3B;AAAA,EACJ;AAAA,EAEA,cAAc,OAAO,MAA8B,cAAuB;AACtE,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,KAAK,OAAO,aAAc,KAAK,kBAAkB,SAAS,CAAE;AAEtG,QAAI,CAAC,KAAK,UAAU,QAAQ,cAAe,OAAM,IAAI,MAAM,wBAAwB;AAEnF,UAAM,MAAW,MAAM,OAAO,QAAQ,EAAE,KAAK,KAAK,SAAS,OAAO,eAAe,cAAc,SAAS,CAAC;AACzG,WAAO,IAAI;AAAA,EACf;AAAA,EAEA,cAAc,OAAO,MAAM,UAAU,OAAO,cAAc;AACtD,UAAM,EAAE,QAAQ,WAAW,eAAe,IAAI,MAAM,iBAAiB,OAAO,SAAS;AACrF,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAE1D,QAAI,iBAAiB;AACrB,QAAI,YAAY,aAAa,QAAQ;AACjC,YAAM,SAAS,MAAM,cAAM,QAAQ,EAAE,KAAK,UAAU,MAAM,CAAC;AAC3D,UAAI,QAAQ,UAAU,QAAQ,GAAI,kBAAiB,OAAO,SAAS,OAAO;AAAA,IAC9E;AAEA,UAAM,MAAW,MAAM,MAAM,MAAM,OAAO;AAAA,MACtC,aAAa;AAAA,QACT;AAAA,QACA,UAAU;AAAA,QACV,SAAS,CAAC,cAAc;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,IACZ,CAAC;AAED,UAAM,OAAO,IAAI;AACjB,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,yCAAyC;AAGvE,UAAM,SAAS,IAAI,cAAM;AAAA,MACrB;AAAA,MACA,MAAM,KAAK;AAAA,MACX,UAAU,aAAa,UAAU,CAAC,WAAW,OAAO;AAAA,MACpD,UAAU;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,UACJ,IAAI,KAAK;AAAA,UACT,aAAa,KAAK;AAAA,UAClB,UAAU,KAAK;AAAA,QACnB;AAAA,MACJ;AAAA,MACA,kBAAkB;AAAA,MAClB,aAAa,EAAE,MAAM,SAAS;AAAA,MAC9B,QAAQ;AAAA,IACZ,CAAC;AACD,UAAM,OAAO,KAAK;AAClB,WAAO,OAAO,SAAS;AAAA,EAC3B;AAAA,EAEA,YAAY,OAAO,OAAO,UAAU,cAAc;AAC9C,QAAI,MAAM,YAAY,SAAS,OAAQ,OAAM,IAAI,MAAM,oBAAoB;AAE3E,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,MAAM,OAAO,aAAc,MAAM,kBAAkB,SAAS,CAAE;AACxG,UAAM,cAAc,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAEhE,QAAI,iBAAiB;AACrB,QAAI,MAAM,UAAU;AAChB,YAAM,SAAS,MAAM,cAAM,SAAS,MAAM,QAAQ;AAClD,UAAI,QAAQ,UAAU,QAAQ,GAAI,kBAAiB,OAAO,SAAS,OAAO;AAAA,IAC9E;AAEA,QAAI;AACA,YAAM,MAAW,MAAM,YAAY,MAAM,OAAO;AAAA,QAC5C,aAAa;AAAA,UACT,MAAM,MAAM;AAAA,UACZ,SAAS,CAAC,cAAc;AAAA,UACxB,UAAU,MAAM,YAAY;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,UACH,UAAU,MAAM,YAAY;AAAA,UAC5B,MAAMC,IAAG,iBAAiB,QAAQ;AAAA,QACtC;AAAA,QACA,QAAQ;AAAA,MACZ,CAAC;AAED,YAAM,QAAQ,IAAI;AAClB,UAAI,CAAC,MAAM,GAAI,OAAM,IAAI,MAAM,+BAA+B;AAG9D,YAAM,SAAS;AACf,YAAM,WAAW;AAAA,QACb,MAAM;AAAA,QACN,QAAQ;AAAA,UACJ,IAAI,MAAM;AAAA,UACV,aAAa,MAAM,eAAe;AAAA,UAClC,UAAU,MAAM,YAAY;AAAA,UAC5B,eAAe,MAAM,iBAAiB;AAAA,QAC1C;AAAA,MACJ;AAAA,IAIJ,SAAS,OAAO;AACZ,YAAM,SAAS;AACf,cAAQ,MAAM,wBAAwB,KAAK;AAC3C,YAAM;AAAA,IACV;AAEA,UAAM,MAAM,KAAK;AACjB,WAAO,MAAM,SAAS;AAAA,EAC1B;AAAA,EAEA,QAAQ,OAAO,KAAK,OAAO,cAAc;AACrC,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,OAAO,SAAS;AAC1D,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAE1D,UAAM,QAAQ,MAAM,cAAM,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,MAAM,CAAC;AAE3D,eAAW,QAAQ,OAAO;AACtB,UAAI,KAAK,UAAU,QAAQ,IAAI;AAC3B,YAAI;AACA,gBAAM,MAAM,MAAM,OAAO,EAAE,QAAQ,KAAK,SAAS,OAAO,GAAG,CAAC;AAAA,QAChE,SAAS,GAAG;AACR,kBAAQ,MAAM,gCAAgC,CAAC;AAAA,QACnD;AAAA,MACJ;AAAA,IACJ;AAEA,UAAM,cAAM,WAAW,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;AAAA,EAChD;AAAA,EAEA,OAAO,OAAO,KAAK,OAAO,cAAc;AACpC,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,OAAO,SAAS;AAC1D,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAE1D,UAAM,QAAQ,MAAM,cAAM,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,MAAM,CAAC;AAE3D,eAAW,QAAQ,OAAO;AACtB,UAAI,KAAK,UAAU,QAAQ,IAAI;AAC3B,YAAI;AACA,gBAAM,MAAM,MAAM,OAAO;AAAA,YACrB,QAAQ,KAAK,SAAS,OAAO;AAAA,YAC7B,aAAa,EAAE,SAAS,KAAK;AAAA,UACjC,CAAC;AAAA,QACL,SAAS,GAAG;AACR,kBAAQ,MAAM,+BAA+B,CAAC;AAAA,QAClD;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,SAAS,OAAO,KAAK,OAAO,cAAc;AACtC,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,OAAO,SAAS;AAC1D,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAE1D,UAAM,QAAQ,MAAM,cAAM,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,MAAM,CAAC;AAE3D,eAAW,QAAQ,OAAO;AACtB,UAAI,KAAK,UAAU,QAAQ,IAAI;AAC3B,YAAI;AACA,gBAAM,MAAM,MAAM,OAAO;AAAA,YACrB,QAAQ,KAAK,SAAS,OAAO;AAAA,YAC7B,aAAa,EAAE,SAAS,MAAM;AAAA,UAClC,CAAC;AAAA,QACL,SAAS,GAAG;AACR,kBAAQ,MAAM,iCAAiC,CAAC;AAAA,QACpD;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,QAAQ,OAAO,IAAI,SAAS,OAAO,cAAc;AAC7C,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,OAAO,SAAS;AAC1D,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAE1D,UAAM,OAAO,MAAM,cAAM,QAAQ,EAAE,KAAK,IAAI,MAAM,CAAC;AACnD,QAAI,CAAC,QAAQ,CAAC,KAAK,UAAU,QAAQ,GAAI,OAAM,IAAI,MAAM,gBAAgB;AAEzE,UAAM,MAAM,MAAM,OAAO;AAAA,MACrB,QAAQ,KAAK,SAAS,OAAO;AAAA,MAC7B,aAAa,EAAE,MAAM,QAAQ;AAAA,IACjC,CAAC;AAED,SAAK,OAAO;AACZ,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK,SAAS;AAAA,EACzB;AAAA,EAEA,MAAM,OAAO,IAAI,aAAa,OAAO,cAAc;AAC/C,UAAM,EAAE,QAAQ,WAAW,eAAe,IAAI,MAAM,iBAAiB,OAAO,SAAS;AACrF,UAAM,QAAQ,OAAO,MAAM,EAAE,SAAS,MAAM,MAAM,OAAO,CAAC;AAG1D,UAAM,OAAO,MAAM,cAAM,QAAQ,EAAE,KAAK,IAAI,MAAM,CAAC;AACnD,QAAI,CAAC,QAAQ,CAAC,KAAK,UAAU,QAAQ,GAAI,OAAM,IAAI,MAAM,8BAA8B;AAGvF,QAAI,yBAA6C;AACjD,QAAI,KAAK,UAAU;AACf,YAAM,YAAY,MAAM,cAAM,QAAQ,EAAE,KAAK,KAAK,UAAU,MAAM,CAAC;AACnE,UAAI,aAAa,UAAU,UAAU,QAAQ,IAAI;AAC7C,iCAAyB,UAAU,SAAS,OAAO;AAAA,MACvD;AAAA,IACJ,OAAO;AAKH,UAAI;AACA,cAAM,QAAQ,MAAM,MAAM,MAAM,IAAI,EAAE,QAAQ,KAAK,SAAS,OAAO,IAAI,QAAQ,UAAU,CAAC;AAC1F,YAAI,MAAM,KAAK,WAAW,MAAM,KAAK,QAAQ,SAAS,GAAG;AACrD,mCAAyB,MAAM,KAAK,QAAQ,KAAK,GAAG;AAAA,QACxD;AAAA,MACJ,SAAS,GAAG;AACR,gBAAQ,KAAK,oCAAoC,CAAC;AAAA,MACtD;AAAA,IACJ;AAGA,QAAI,oBAAoB;AAExB,QAAI,eAAe,gBAAgB,QAAQ;AACvC,YAAM,YAAY,MAAM,cAAM,QAAQ,EAAE,KAAK,aAAa,MAAM,CAAC;AACjE,UAAI,CAAC,aAAa,CAAC,UAAU,UAAU,QAAQ,GAAI,OAAM,IAAI,MAAM,yCAAyC;AAC5G,0BAAoB,UAAU,SAAS,OAAO;AAAA,IAClD;AAGA,UAAM,MAAM,MAAM,OAAO;AAAA,MACrB,QAAQ,KAAK,SAAS,OAAO;AAAA,MAC7B,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,QAAQ;AAAA,IACZ,CAAC;AAGD,SAAK,WAAW,gBAAgB,UAAU,CAAC,cAAc,OAAO,IAAIC,UAAS,MAAM,SAAS,WAAW;AACvG,UAAM,KAAK,KAAK;AAEhB,WAAO,KAAK,SAAS;AAAA,EACzB;AAAA,EAEA,aAAa,OAAO,OAAO,cAAc;AACrC,QAAI,CAAC,UAAW;AAChB,UAAM,EAAE,OAAO,IAAI,MAAM,iBAAiB,OAAO,SAAS;AAC1D,UAAM,UAAU,MAAM,gBAAe,SAAS,SAAS;AACvD,QAAI,SAAS,UAAU,aAAa,YAAY,QAAQ,SAAS,QAAQ,aAAa;AAClF,YAAM,QAAQ,QAAQ,SAAS,OAAO;AACtC,UAAI,OAAO,UAAU,YAAY,kBAAkB,OAAO;AACtD,cAAM,OAAO,YAAY,MAAM,YAAY;AAAA,MAC/C;AAAA,IACJ;AAAA,EACJ;AACJ;;;AClkBA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,aAAY;AAwCZ,IAAM,cAAc,CAAC,QAAgB,YAAiD;AACzF,QAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,OAAO,SAAS,YAAY,SAAS;AACtC,WAAO,8BAA8B,MAAM;AAAA,EAC/C;AAEA,QAAM,EAAE,QAAQ,UAAU,IAAI,OAAO,SAAS;AAC9C,MAAI;AAEJ,MAAI,SAAS,kBAAkB,MAAM;AACjC,sBAAkB,KAAK,MAAM,QAAQ,OAAO,QAAQ,IAAI,GAAI;AAAA,EAChE,WAAW,OAAO,SAAS,WAAW,UAAU;AAC5C,sBAAkB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,QAAQ;AAAA,EAC9D,OAAO;AACH,sBAAkB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AAAA,EACtD;AAEA,QAAM,YAAYC,QAAO,WAAW,UAAU,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,eAAe,EAAE,EAAE,OAAO,KAAK;AACzG,QAAM,QAAQ,OAAO,KAAK,GAAG,eAAe,IAAI,SAAS,EAAE,EAAE,SAAS,WAAW;AACjF,SAAO,8BAA8B,MAAM,UAAU,KAAK;AAC9D;AAiBO,IAAM,gBAAgB,OACzB,SAC4D;AAC5D,MAAI;AAGJ,MAAI,OAAO,SAAS,UAAU;AAC1B,UAAM,MAAM,MAAM,cAAM,SAAS,IAAI;AACrC,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mBAAmB,IAAI,EAAE;AACnD,YAAQ;AAAA,EACZ,WAAW,cAAc,MAAM;AAE3B,YAAQ;AAAA,EACZ,OAAO;AACH,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACrD;AAEA,MAAI,MAAM,YAAY,SAAS,QAAQ;AACnC,UAAM,IAAI,MAAM,sBAAsB;AAAA,EAC1C;AAGA,QAAM,WAAW,MAAM,UAAU,SAAS,WAAW,sBAAsB;AAC3E,QAAM,YAAY,MAAM,kBAAkB,SAAS;AAGnD,SAAO,MAAM,SAAS,WAAW,OAAO,SAAS;AACrD;AAkBO,IAAM,gBAAgB,OACzB,SACkG;AAClG,MAAI;AAGJ,MAAI,OAAO,SAAS,UAAU;AAC1B,UAAM,MAAM,MAAM,cAAM,SAAS,IAAI;AACrC,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mBAAmB,IAAI,EAAE;AACnD,YAAQ;AAAA,EACZ,WAAW,cAAc,MAAM;AAE3B,YAAQ;AAAA,EACZ,OAAO;AACH,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACrD;AAEA,MAAI,MAAM,YAAY,SAAS,QAAQ;AACnC,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAClD;AAEA,QAAM,SAAS,eAAe;AAC9B,QAAMC,gBAAe,OAAO,QAAQ;AACpC,QAAM,eAAe,MAAM,UAAU,QAAQ;AAG7C,MAAI,iBAAiB,SAAS;AAC1B,UAAM,WAAWC,MAAK,KAAKD,eAAc,MAAM,YAAY,IAAI;AAE/D,QAAI,CAACE,IAAG,WAAW,QAAQ,GAAG;AAC1B,YAAM,IAAI,MAAM,iCAAiC,QAAQ,EAAE;AAAA,IAC/D;AAEA,WAAO,OAAO,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM,YAAY;AAAA,MACxB,MAAM,MAAM,YAAY;AAAA,MACxB,UAAU;AAAA,IACd,CAAC;AAAA,EACL;AAGA,MAAI,iBAAiB,UAAU;AAC3B,UAAM,aAAaD,MAAK,KAAKD,eAAc,WAAW,QAAQ;AAC9D,UAAM,WAAW,GAAG,MAAM,GAAG,GAAGC,MAAK,QAAQ,MAAM,IAAI,CAAC;AACxD,UAAM,iBAAiBA,MAAK,KAAK,YAAY,QAAQ;AAGrD,QAAIC,IAAG,WAAW,cAAc,GAAG;AAC/B,YAAM,QAAQA,IAAG,SAAS,cAAc;AAGxC,UAAI,MAAM,SAAS,MAAM,YAAY,aAAa;AAC9C,eAAO,OAAO,OAAO;AAAA,UACjB,MAAM;AAAA,UACN,MAAM,MAAM;AAAA,UACZ,MAAM,MAAM,YAAY;AAAA,UACxB,MAAM,MAAM,YAAY;AAAA,UACxB,UAAU;AAAA,QACd,CAAC;AAAA,MACL;AAGA,MAAAA,IAAG,WAAW,cAAc;AAAA,IAChC;AAGA,UAAM,YAAY,MAAM,kBAAkB,SAAS;AACnD,UAAM,EAAE,OAAO,IAAI,MAAM,oBAAoB,WAAW,OAAO,SAAS;AAGxE,QAAI,CAACA,IAAG,WAAW,UAAU,GAAG;AAC5B,MAAAA,IAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,IAChD;AAGA,UAAM,WAAW,GAAG,cAAc;AAClC,UAAM,cAAcA,IAAG,kBAAkB,QAAQ;AAEjD,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AACzC,aAAO,KAAK,WAAW;AACvB,kBAAY,GAAG,UAAU,OAAO;AAChC,kBAAY,GAAG,SAAS,MAAM;AAC9B,aAAO,GAAG,SAAS,MAAM;AAAA,IAC7B,CAAC;AAGD,IAAAA,IAAG,WAAW,UAAU,cAAc;AAEtC,WAAO,OAAO,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM,YAAY;AAAA,MACxB,MAAM,MAAM,YAAY;AAAA,MACxB,UAAU;AAAA,IACd,CAAC;AAAA,EACL;AAEA,QAAM,IAAI,MAAM,yBAAyB,YAAY,EAAE;AAC3D;;;AT3MA,IAAM,cAAc,OAAO,KAAqB,UAAuG;AAEnJ,QAAM,YAAY,IAAI,QAAQ,iBAAiB;AAE/C,MAAI,CAAC,aAAa,cAAc,SAAS;AACrC,WAAO,EAAE,UAAU,qBAAqB;AAAA,EAC5C;AAGA,QAAM,UAAU,MAAM,gBAAe,QAAQ,EAAE,KAAK,WAAW,MAAM,CAAC;AACtE,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC7C;AAEA,MAAI,QAAQ,SAAS,aAAa,SAAU,QAAO,EAAE,UAAU,qBAAqB,WAAW,QAAQ,IAAI,SAAS,EAAE;AAGtH,SAAO,EAAE,UAAU,qBAAqB;AAC5C;AAGO,IAAM,kBAAkB,OAAO,KAAqB,QAAwC;AAC/F,QAAM,SAAS,IAAI,MAAM;AAGzB,MAAI;AACA,mBAAe;AAAA,EACnB,SAAS,OAAO;AACZ,YAAQ,MAAM,4CAA4C,KAAK;AAC/D,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,2CAA2C,CAAC;AACzF;AAAA,EACJ;AAEA,MAAI,CAAC,QAAQ;AACT,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,iCAAiC,CAAC;AAC/E;AAAA,EACJ;AAEA,MAAI;AACA,UAAM,SAAS,eAAe;AAC9B,UAAM,cAAc,MAAM,oBAAoB,GAAG;AACjD,UAAM,EAAE,KAAK,MAAM,IAAI;AACvB,UAAMC,gBAAe,OAAO,QAAQ;AAGpC,QAAI,CAAC,cAAc,YAAY,gBAAgB,eAAe,EAAE,SAAS,MAAM,GAAG;AAC9E,cAAQ,QAAQ;AAAA,QACZ,KAAK,cAAc;AACf,gBAAM,EAAE,UAAAC,UAAS,IAAI,IAAI;AACzB,cAAIA,cAAa,UAAU;AACvB,kBAAM,EAAE,UAAU,cAAc,YAAY,IAAI,OAAO,SAAS,UAAU,CAAC;AAC3E,gBAAI,CAAC,YAAY,CAAC,aAAc,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,wBAAwB,CAAC;AAE7G,kBAAM,EAAE,QAAAC,QAAO,IAAI,UAAQ,YAAY;AACvC,kBAAM,eAAe,IAAIA,QAAO,KAAK,OAAO,UAAU,cAAc,WAAW;AAG/E,kBAAM,QAAQ,OAAO,KAAK,KAAK,UAAU,EAAE,MAAM,CAAC,CAAC,EAAE,SAAS,QAAQ;AACtE,kBAAM,MAAM,aAAa,gBAAgB;AAAA,cACrC,aAAa;AAAA,cACb,OAAO,CAAC,yCAAyC,gDAAgD;AAAA,cACjG;AAAA,cACA,QAAQ;AAAA;AAAA,YACZ,CAAC;AACD,mBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,sBAAsB,MAAM,EAAE,IAAI,EAAE,CAAC;AAAA,UAC7F;AACA,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,mBAAmB,CAAC;AAAA,QAC5E;AAAA,QACA,KAAK,YAAY;AACb,gBAAM,EAAE,MAAM,MAAM,IAAI,IAAI;AAC5B,cAAI,CAAC,KAAM,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,eAAe,CAAC;AAK/E,gBAAM,EAAE,UAAU,cAAc,YAAY,IAAI,OAAO,SAAS,UAAU,CAAC;AAC3E,gBAAM,EAAE,QAAAA,QAAO,IAAI,UAAQ,YAAY;AACvC,gBAAM,eAAe,IAAIA,QAAO,KAAK,OAAO,UAAU,cAAc,WAAW;AAE/E,gBAAM,EAAE,OAAO,IAAI,MAAM,aAAa,SAAS,IAAc;AAC7D,uBAAa,eAAe,MAAM;AAGlC,gBAAM,SAASA,QAAO,OAAO,EAAE,SAAS,MAAM,MAAM,aAAa,CAAC;AAClE,gBAAM,WAAW,MAAM,OAAO,SAAS,IAAI;AAG3C,gBAAM,WAAW,MAAM,gBAAe,QAAQ,EAAE,OAAO,yBAAyB,SAAS,KAAK,OAAO,qBAAqB,SAAS,CAAC;AACpI,cAAI,UAAU;AACV,qBAAS,SAAS,OAAO,cAAc;AACvC,qBAAS,aAAa,UAAU;AAChC,kBAAM,SAAS,KAAK;AAAA,UACxB,OAAO;AACH,kBAAM,gBAAe,OAAO;AAAA,cACxB;AAAA,cACA,MAAM,SAAS,KAAK,QAAQ;AAAA,cAC5B,UAAU;AAAA,gBACN,UAAU;AAAA,gBACV,QAAQ;AAAA,kBACJ,OAAO,SAAS,KAAK;AAAA,kBACrB,aAAa;AAAA,gBACjB;AAAA,cACJ;AAAA,YACJ,CAAC;AAAA,UACL;AAGA,cAAI,UAAU,gBAAgB,WAAW;AACzC,iBAAO,IAAI,KAAK,mFAAmF;AAAA,QACvG;AAAA,QACA,KAAK,gBAAgB;AACjB,gBAAM,WAAW,MAAM,gBAAe,KAAK,EAAE,MAAM,CAAC;AACpD,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YACxB,QAAQ;AAAA,YACR,MAAM;AAAA,cACF,UAAU,SAAS,IAAI,QAAM;AAAA,gBACzB,IAAI,EAAE,IAAI,SAAS;AAAA,gBACnB,MAAM,EAAE;AAAA,gBACR,OAAO,EAAE,SAAS,QAAQ,SAAS;AAAA,gBACnC,UAAU,EAAE,SAAS;AAAA,cACzB,EAAE;AAAA,YACN;AAAA,UACJ,CAAC;AAAA,QACL;AAAA,QACA,KAAK,iBAAiB;AAClB,gBAAM,EAAE,GAAG,IAAI,IAAI;AACnB,gBAAM,UAAU,MAAM,gBAAe,QAAQ,EAAE,KAAK,IAAI,MAAM,CAAC;AAC/D,cAAI,CAAC,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,oBAAoB,CAAC;AAGvF,cAAI,QAAQ,SAAS,aAAa,UAAU;AACxC,gBAAI;AACA,oBAAM,oBAAoB,YAAY,OAAO,QAAQ,IAAI,SAAS,CAAC;AAAA,YACvE,SAAS,GAAG;AACR,sBAAQ,MAAM,kCAAkC,CAAC;AAAA,YAErD;AAAA,UACJ;AAEA,gBAAM,gBAAe,UAAU,EAAE,KAAK,IAAI,MAAM,CAAC;AACjD,gBAAM,cAAM,WAAW,EAAE,OAAO,kBAAkB,GAAG,CAAC;AACtD,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,kBAAkB,CAAC;AAAA,QAC3E;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,EAAE,UAAU,UAAU,IAAI,MAAM,YAAY,KAAK,KAAK;AAE5D,YAAQ,QAAQ;AAAA;AAAA,MAEZ,KAAK,QAAQ;AACT,YAAI,IAAI,WAAW,MAAO,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,mBAAmB,CAAC;AAClG,cAAM,YAAoB,gBAAgB,UAAU,IAAI,KAAK;AAC7D,YAAI,CAAC,UAAU,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,qBAAqB,CAAC;AAElG,cAAM,EAAE,UAAU,OAAO,QAAQ,IAAI,UAAU;AAI/C,YAAI;AACA,gBAAM,SAAS,KAAK,YAAY,QAAQ,OAAO,SAAS;AAAA,QAC5D,SAAS,GAAG;AACR,kBAAQ,MAAM,eAAe,CAAC;AAAA,QAElC;AAGA,cAAM,QAAiC;AAAA,UACnC;AAAA,UACA,iBAAiB,SAAS;AAAA,UAC1B,kBAAkB,aAAa;AAAA,UAC/B,UAAU,aAAa,UAAU,CAAC,WAAW,OAAO;AAAA,UACpD,WAAW;AAAA,QACf;AACA,YAAI,QAAS,OAAM,MAAM,EAAE,KAAK,QAAQ;AAExC,cAAM,QAAQ,MAAM,cAAM,KAAK,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAG,KAAK,GAAG,GAAG,MAAM,CAAC;AAChF,cAAM,aAAa,MAAM,QAAQ,IAAI,MAAM,IAAI,UAAQ,KAAK,SAAS,CAAC,CAAC;AAEvE,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,mBAAmB,MAAM,EAAE,OAAO,YAAY,SAAS,MAAM,WAAW,MAAM,EAAE,CAAC;AAC9H;AAAA,MACJ;AAAA;AAAA,MAGA,KAAK,UAAU;AACX,cAAM,aAAqB,kBAAkB,UAAU,IAAI,KAAK;AAChE,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,iBAAiB,CAAC;AAC/F,cAAM,EAAE,GAAG,UAAU,OAAO,QAAQ,IAAI,WAAW;AAGnD,YAAI,CAAC,SAAS;AACV,cAAI;AAAE,kBAAM,SAAS,OAAO,GAAG,OAAO,SAAS;AAAA,UAAG,SAAS,GAAG;AAAE,oBAAQ,MAAM,sBAAsB,CAAC;AAAA,UAAG;AAAA,QAC5G;AAGA,cAAM,QAAiC;AAAA,UACnC;AAAA,UACA,iBAAiB,SAAS;AAAA,UAC1B,kBAAkB,aAAa;AAAA,UAC/B,WAAW,UAAU,EAAE,KAAK,KAAK,IAAI;AAAA,UACrC,MAAM,EAAE,QAAQ,GAAG,UAAU,IAAI;AAAA,QACrC;AACA,YAAI,YAAY,aAAa,OAAQ,OAAM,WAAW;AAEtD,cAAM,QAAQ,MAAM,cAAM,KAAK,OAAO,CAAC,GAAG,EAAE,OAAO,MAAM,EAAE,WAAW,GAAG,EAAE,CAAC;AAC5E,cAAM,aAAa,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAK,EAAE,SAAS,CAAC,CAAC;AAEjE,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,WAAW,MAAM,EAAE,OAAO,WAAW,EAAE,CAAC;AAAA,MAChG;AAAA;AAAA,MAGA,KAAK,UAAU;AACX,YAAI,IAAI,WAAW,OAAQ,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,oBAAoB,CAAC;AACpG,cAAM,OAAO,WAAW;AAAA,UACpB,WAAW;AAAA,UACX,aAAa,OAAO,SAAS,uBAAuB;AAAA,UACpD,WAAWC,MAAK,KAAKH,eAAc,MAAM;AAAA,UACzC,gBAAgB;AAAA,QACpB,CAAC;AACD,YAAI,CAACI,IAAG,WAAWD,MAAK,KAAKH,eAAc,MAAM,CAAC,EAAG,CAAAI,IAAG,UAAUD,MAAK,KAAKH,eAAc,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAEtH,cAAM,CAAC,QAAQ,KAAK,IAAI,MAAM,IAAI,QAA+C,CAAC,SAAS,WAAW;AAClG,eAAK,MAAM,KAAK,CAAC,KAAKK,SAAQC,WAAU;AACpC,gBAAI,IAAK,QAAO,GAAG;AAAA,gBAAQ,SAAQ,CAACD,SAAQC,MAAK,CAAC;AAAA,UACtD,CAAC;AAAA,QACL,CAAC;AAED,cAAM,mBAAmB,CAACA,WAA4B;AAClD,iBAAO,OAAOA,MAAK,EAAE,KAAK,EAAE,QAAQ,UAAQ;AACxC,gBAAI,QAAQF,IAAG,WAAW,KAAK,QAAQ,EAAG,CAAAA,IAAG,OAAO,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,UACtF,CAAC;AAAA,QACL;AAEA,cAAM,YAAY,CAAC,MAAsC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK;AACxF,cAAM,SAAS,CAAC,MAAqC,SAAS,UAAU,CAAC,KAAK,KAAK,EAAE;AAErF,cAAM,aAAqB,kBAAkB,UAAU;AAAA,UACnD,YAAY,OAAO,OAAO,UAAU;AAAA,UACpC,aAAa,OAAO,OAAO,WAAW;AAAA,UACtC,SAAS,UAAU,OAAO,OAAO,KAAK;AAAA,UACtC,UAAU,UAAU,OAAO,QAAQ;AAAA,UACnC,UAAU,OAAO,OAAO,QAAQ;AAAA,UAChC,UAAU,UAAU,OAAO,QAAQ;AAAA,UACnC,UAAU,UAAU,OAAO,QAAQ,KAAK;AAAA,QAC5C,CAAC;AAED,YAAI,CAAC,WAAW,SAAS;AACrB,2BAAiB,KAAK;AACtB,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,WAAW,MAAM,OAAO,CAAC,EAAE,QAAQ,CAAC;AAAA,QAC5F;AAEA,cAAM,EAAE,YAAY,aAAa,SAAS,UAAU,UAAU,iBAAiB,UAAU,SAAS,IAAI,WAAW;AAEjH,YAAI,kBAAkB;AAGtB,cAAM,cAAcD,MAAK,KAAKH,eAAc,QAAQ,SAAS;AAE7D,YAAI,CAAC,iBAAiB;AAElB,cAAI,eAAe,EAAG,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,uCAAuC,CAAC;AAErG,cAAI,YAAY,CAAC,iBAAiB,UAAU,OAAO,SAAS,gBAAgB,GAAG;AAC3E,6BAAiB,KAAK;AACtB,mBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,aAAa,QAAQ,eAAe,CAAC;AAAA,UAC7F;AAGA,gBAAM,QAAQ,MAAM,SAAS,SAAS,OAAO,SAAS;AACtD,cAAI,MAAM,cAAc,kBAAkB,MAAM,cAAc;AAC1D,6BAAiB,KAAK;AACtB,mBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,yBAAyB,CAAC;AAAA,UAClF;AAGA,4BAAkB,OAAO,WAAW;AACpC,gBAAM,YAAYG,MAAK,KAAK,aAAa,eAAe;AACxD,UAAAC,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAG3C,gBAAM,WAAW;AAAA,YACb;AAAA,YACA;AAAA,YACA,cAAc,SAAS;AAAA,YACvB,MAAM;AAAA,YACN,UAAU,aAAa,UAAU,CAAC,WAAW,OAAO;AAAA,YACpD,UAAU;AAAA,YACV,UAAU;AAAA,YACV;AAAA,UACJ;AACA,UAAAA,IAAG,cAAcD,MAAK,KAAK,WAAW,eAAe,GAAG,KAAK,UAAU,QAAQ,CAAC;AAAA,QACpF;AAGA,YAAI,iBAAiB;AACjB,gBAAM,YAAYA,MAAK,KAAK,aAAa,eAAe;AAExD,cAAI,CAACC,IAAG,WAAW,SAAS,GAAG;AAC3B,6BAAiB,KAAK;AAEtB,mBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,sCAAsC,CAAC;AAAA,UAC/F;AAEA,cAAI;AACA,kBAAM,YAAY,MAAM,QAAQ,MAAM,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI,MAAM;AACtE,gBAAI,CAAC,UAAW,OAAM,IAAI,MAAM,wBAAwB;AAGxD,kBAAM,WAAWD,MAAK,KAAK,WAAW,QAAQ,UAAU,EAAE;AAC1D,YAAAC,IAAG,WAAW,UAAU,UAAU,QAAQ;AAI1C,kBAAM,gBAAgBA,IAAG,YAAY,SAAS,EAAE,OAAO,OAAK,EAAE,WAAW,OAAO,CAAC;AAGjF,gBAAI,cAAc,WAAW,aAAa;AAEtC,oBAAM,WAAWD,MAAK,KAAK,WAAW,eAAe;AACrD,oBAAM,OAAO,KAAK,MAAMC,IAAG,aAAa,UAAU,OAAO,CAAC;AAG1D,oBAAM,gBAAgBD,MAAK,KAAK,WAAW,WAAW;AACtD,oBAAM,cAAcC,IAAG,kBAAkB,aAAa;AAEtD,uBAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AAClC,sBAAM,QAAQD,MAAK,KAAK,WAAW,QAAQ,CAAC,EAAE;AAC9C,sBAAM,OAAOC,IAAG,aAAa,KAAK;AAClC,4BAAY,MAAM,IAAI;AAAA,cAG1B;AACA,0BAAY,IAAI;AAKhB,oBAAM,QAAQ,IAAI,cAAM;AAAA,gBACpB,OAAO,KAAK;AAAA,gBACZ,kBAAkB,KAAK,aAAa;AAAA,gBACpC,UAAU,EAAE,MAAM,KAAK,aAAa;AAAA,gBACpC,MAAM,KAAK;AAAA,gBACX,UAAU,KAAK;AAAA,gBACf,OAAO;AAAA,gBACP,aAAa,EAAE,MAAM,QAAQ,aAAa,KAAK,UAAU,MAAM,KAAK,UAAU,MAAM,GAAG;AAAA;AAAA,gBACvF,QAAQ;AAAA,gBACR,cAAc;AAAA,gBACd;AAAA,cACJ,CAAC;AAGD,kBAAI,KAAK,iBAAiB,WAAW,MAAM,YAAY,SAAS,QAAQ;AACpE,sBAAM,YAAY,OAAOD,MAAK,KAAK,SAAS,OAAO,MAAM,GAAG,GAAG,UAAU;AAAA,cAC7E;AAEA,oBAAM,MAAM,KAAK;AAGjB,kBAAI;AACA,sBAAM,OAAO,MAAM,SAAS,WAAW,OAAO,eAAe,KAAK,SAAS;AAG3E,gBAAAC,IAAG,OAAO,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAErD,sBAAM,WAAW,MAAM,SAAS,SAAS,KAAK,OAAO,KAAK,SAAS;AACnE,oBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,mBAAmB,MAAM,EAAE,MAAM,mBAAmB,SAAS,OAAO,MAAM,GAAG,GAAG,KAAK,GAAG,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;AAAA,cAE3K,SAAS,KAAK;AAEV,sBAAM,cAAM,UAAU,EAAE,KAAK,MAAM,IAAI,CAAC;AACxC,sBAAM;AAAA,cACV;AAAA,YAEJ,OAAO;AAEH,oBAAM,WAAW,MAAM,SAAS,SAAS,OAAO,SAAS;AAEzD,kBAAI,eAAe,GAAG;AAClB,oBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,kBAAkB,MAAM,EAAE,MAAM,kBAAkB,SAAS,gBAAgB,GAAG,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;AAAA,cACjK,OAAO;AACH,oBAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,kBAAkB,MAAM,EAAE,MAAM,kBAAkB,SAAS,iBAAiB,WAAW,GAAG,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;AAAA,cAC7K;AAAA,YACJ;AAAA,UAEJ,SAAS,GAAG;AACR,6BAAiB,KAAK;AAItB,kBAAM;AAAA,UACV;AACA;AAAA,QACJ;AAGA,yBAAiB,KAAK;AACtB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,yBAAyB,CAAC;AAAA,MAClF;AAAA;AAAA,MAGA,KAAK,gBAAgB;AACjB,cAAM,aAAqB,uBAAuB,UAAU,IAAI,IAAI;AACpE,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,WAAW,MAAM,OAAO,CAAC,EAAE,QAAQ,CAAC;AACjH,cAAM,EAAE,MAAM,SAAS,IAAI,WAAW;AAEtC,cAAM,OAAO,MAAM,SAAS,aAAa,MAAM,YAAY,MAAM,OAAO,SAAS;AACjF,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,kBAAkB,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,MAC1F;AAAA;AAAA,MAGA,KAAK,UAAU;AACX,cAAM,aAAqB,kBAAkB,UAAU,IAAI,KAAK;AAChE,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,aAAa,CAAC;AAE3F,cAAM,EAAE,GAAG,IAAI,WAAW;AAC1B,cAAM,QAAQ,MAAM,cAAM,SAAS,EAAE;AACrC,YAAI,CAAC,MAAO,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,YAAY,CAAC;AAG7E,cAAM,eAAe,MAAM,UAAU,SAAS,WAAW,sBAAsB;AAC/E,cAAM,gBAAgB,MAAM,mBAAmB,MAAM,iBAAiB,SAAS,IAAI;AAEnF,YAAI;AACA,gBAAM,aAAa,MAAM,CAAC,EAAE,GAAG,OAAO,aAAa;AAAA,QACvD,SAAS,GAAG;AACR,kBAAQ,MAAM,0BAA0B,CAAC;AAAA,QAC7C;AAEA,cAAM,YAAY,oBAAI,KAAK;AAC3B,cAAM,MAAM,KAAK;AACjB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,kBAAkB,MAAM,KAAK,CAAC;AAAA,MACtF;AAAA;AAAA,MAGA,KAAK,mBAAmB;AACpB,cAAM,aAAqB,kBAAkB,UAAU,IAAI,KAAK;AAChE,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,aAAa,CAAC;AAC3F,cAAM,EAAE,GAAG,IAAI,WAAW;AAE1B,cAAM,SAAS,OAAO,CAAC,EAAE,GAAG,OAAO,SAAS;AAC5C,cAAM,QAAQ,MAAM,SAAS,SAAS,OAAO,SAAS;AACtD,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,WAAW,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;AAAA,MAClG;AAAA;AAAA,MAGA,KAAK,SAAS;AACV,cAAM,QAAQ,MAAM,SAAS,SAAS,OAAO,SAAS;AACtD,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACxB,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,MAAM,EAAE,aAAa,MAAM,aAAa,cAAc,MAAM,cAAc,kBAAkB,KAAK,IAAI,GAAG,MAAM,eAAe,MAAM,WAAW,GAAG,YAAY,MAAM,eAAe,IAAI,KAAK,MAAO,MAAM,cAAc,MAAM,eAAgB,GAAG,IAAI,EAAE;AAAA,UACrP,WAAW,EAAE,SAAS,MAAM;AAAA,QAChC,CAAC;AAAA,MACL;AAAA;AAAA,MAGA,KAAK,SAAS;AAEV,YAAI;AACA,gBAAM,EAAE,UAAU,eAAe,WAAW,eAAe,IAAI,MAAM,YAAY,KAAK,KAAK;AAC3F,gBAAM,cAAc,UAAU,OAAO,cAAc;AAAA,QACvD,SAAS,GAAG;AACR,kBAAQ,MAAM,qBAAqB,CAAC;AAAA,QACxC;AAGA,cAAM,QAAiC;AAAA,UACnC;AAAA,UACA,iBAAiB,SAAS;AAAA,UAC1B,kBAAkB,aAAa;AAAA,UAC/B,WAAW,EAAE,KAAK,KAAK;AAAA,QAC3B;AAEA,cAAM,QAAQ,MAAM,cAAM,KAAK,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,GAAG,EAAE,CAAC;AACrE,cAAM,aAAa,MAAM,QAAQ,IAAI,MAAM,IAAI,UAAQ,KAAK,SAAS,CAAC,CAAC;AAEvE,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACxB,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,MAAM,EAAE,OAAO,YAAY,SAAS,MAAM;AAAA,QAC9C,CAAC;AAAA,MACL;AAAA;AAAA,MAGA,KAAK,WAAW;AACZ,cAAM,cAAsB,kBAAkB,UAAU,IAAI,KAAK;AACjE,YAAI,CAAC,YAAY,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,aAAa,CAAC;AAC5F,cAAM,EAAE,GAAG,IAAI,YAAY;AAC3B,cAAM,QAAQ,MAAM,cAAM,SAAS,EAAE;AACrC,YAAI,CAAC,MAAO,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,YAAY,CAAC;AAG7E,YAAI,iBAAiB,MAAM;AAC3B,YAAI,gBAAgB;AAChB,gBAAM,SAAS,MAAM,cAAM,SAAS,cAAc;AAClD,cAAI,QAAQ,WAAW;AACnB,6BAAiB;AAAA,UACrB;AAAA,QACJ;AAGA,cAAM,eAAe,MAAM,UAAU,SAAS,WAAW,sBAAsB;AAC/E,cAAM,gBAAgB,MAAM,mBAAmB,MAAM,iBAAiB,SAAS,IAAI;AAEnF,YAAI;AACA,gBAAM,aAAa,QAAQ,CAAC,EAAE,GAAG,OAAO,aAAa;AAErD,cAAI,mBAAmB,MAAM,UAAU;AACnC,kBAAM,aAAa,KAAK,IAAI,gBAAgB,SAAS,KAAK,MAAM,OAAO,aAAa;AAAA,UACxF;AAAA,QACJ,SAAS,GAAG;AACR,kBAAQ,MAAM,4BAA4B,CAAC;AAAA,QAC/C;AAGA,cAAM,YAAY;AAClB,cAAM,WAAW;AACjB,cAAM,MAAM,KAAK;AAEjB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACxB,QAAQ;AAAA,UACR,SAAS,mBAAmB,QAAQ,MAAM,aAAa,OACjD,iDACA;AAAA,UACN,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AAAA;AAAA,MAGA,KAAK,QAAQ;AACT,cAAM,WAAmB,eAAe,UAAU,IAAI,IAAI;AAC1D,YAAI,CAAC,SAAS,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,eAAe,CAAC;AAC3F,cAAM,EAAE,KAAK,eAAe,IAAI,SAAS;AAEzC,cAAM,QAA0B,CAAC;AAEjC,cAAM,oBAAoB,mBAAmB,UAAU,CAAC,iBAAiB,OAAO;AAEhF,mBAAW,MAAM,KAAK;AAClB,cAAI;AACA,kBAAM,OAAO,MAAM,SAAS,KAAK,IAAI,mBAAmB,OAAO,SAAS;AACxE,kBAAM,KAAK,IAAI;AAAA,UACnB,SAAS,GAAG;AACR,oBAAQ,MAAM,uBAAuB,EAAE,IAAI,CAAC;AAAA,UAEhD;AAAA,QACJ;AAEA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC;AAAA,MAClF;AAAA;AAAA,MAGA,KAAK,UAAU;AACX,cAAM,aAAqB,iBAAiB,UAAU,EAAE,IAAI,IAAI,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC;AACvF,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,eAAe,CAAC;AAC7F,cAAM,EAAE,IAAI,QAAQ,IAAI,WAAW;AACnC,cAAM,OAAO,MAAM,SAAS,OAAO,IAAI,SAAS,OAAO,SAAS;AAChE,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,WAAW,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,MACnF;AAAA;AAAA,MAGA,KAAK,aAAa;AACd,cAAM,aAAqB,qBAAqB,UAAU,IAAI,KAAK;AACnE,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,iBAAiB,CAAC;AAC/F,cAAM,EAAE,GAAG,IAAI,WAAW;AAC1B,cAAM,QAAQ,MAAM,cAAM,SAAS,EAAE;AACrC,YAAI,CAAC,MAAO,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,YAAY,CAAC;AAG7E,cAAM,eAAe,MAAM,UAAU,SAAS,WAAW,sBAAsB;AAC/E,cAAM,gBAAgB,MAAM,mBAAmB,MAAM,iBAAiB,SAAS,IAAI;AAEnF,cAAM,SAAS,MAAM,aAAa,aAAa,OAAO,aAAa;AACnE,YAAI,UAAU,gBAAgB,YAAY;AAC1C,eAAO,KAAK,GAAG;AACf;AAAA,MACJ;AAAA;AAAA,MAGA,KAAK,SAAS;AACV,cAAM,aAAqB,iBAAiB,UAAU,IAAI,KAAK;AAC/D,YAAI,CAAC,WAAW,QAAS,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,iBAAiB,CAAC;AAC/F,cAAM,EAAE,GAAG,IAAI,WAAW;AAC1B,cAAM,QAAQ,MAAM,cAAM,SAAS,EAAE;AACrC,YAAI,CAAC,MAAO,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,YAAY,CAAC;AAG7E,cAAM,eAAe,MAAM,UAAU,SAAS,WAAW,sBAAsB;AAC/E,cAAM,gBAAgB,MAAM,mBAAmB,MAAM,iBAAiB,SAAS,IAAI;AAEnF,cAAM,EAAE,QAAQ,MAAM,KAAK,IAAI,MAAM,aAAa,WAAW,OAAO,aAAa;AAEjF,cAAM,eAAe,mCAAmC,MAAM,IAAI;AAClE,YAAI,UAAU,uBAAuB,qBAAqB,YAAY,GAAG;AACzE,YAAI,UAAU,gBAAgB,IAAI;AAElC,YAAI,KAAM,KAAI,UAAU,kBAAkB,IAAI;AAE9C,eAAO,KAAK,GAAG;AACf;AAAA,MACJ;AAAA,MAEA;AACI,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,mBAAmB,MAAM,GAAG,CAAC;AAAA,IAClF;AAAA,EACJ,SAAS,OAAgB;AACrB,YAAQ,MAAM,6CAA6C,MAAM,KAAK,KAAK;AAE3E,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,KAAK,SAAS,iBAAiB,QAAQ,MAAM,UAAU,gBAAgB,CAAC;AAAA,EAC3G;AACJ;","names":["path","fs","mongoose","mongoose","Schema","crypto","crypto","fs","mongoose","fs","sharp","owner","folderIds","mongoose","fs","path","mongoose","STORAGE_PATH","path","fs","mongoose","fs","path","crypto","crypto","STORAGE_PATH","path","fs","STORAGE_PATH","provider","google","path","fs","fields","files"]}
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "next-file-manager",
3
+ "version": "0.1.0",
4
+ "description": "File storage and management for Next.js applications",
5
+ "type": "module",
6
+ "exports": {
7
+ "./server": {
8
+ "types": "./dist/server/index.d.ts",
9
+ "import": "./dist/server/index.js"
10
+ },
11
+ "./client": {
12
+ "types": "./dist/client/index.d.ts",
13
+ "import": "./dist/client/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "lint": "eslint src/",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest",
25
+ "test:coverage": "vitest --coverage",
26
+ "test:integration": "vitest -c vitest.integration.config.ts"
27
+ },
28
+ "peerDependencies": {
29
+ "mongoose": ">=7.0.0",
30
+ "next": ">=14.0.0",
31
+ "react": ">=18.0.0",
32
+ "react-dom": ">=18.0.0"
33
+ },
34
+ "dependencies": {
35
+ "@dnd-kit/core": "^6.3.1",
36
+ "@dnd-kit/sortable": "^10.0.0",
37
+ "@dnd-kit/utilities": "^3.2.2",
38
+ "@radix-ui/react-alert-dialog": "^1.1.15",
39
+ "@radix-ui/react-context-menu": "^2.2.16",
40
+ "@radix-ui/react-dialog": "^1.1.15",
41
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
42
+ "@radix-ui/react-label": "^2.1.8",
43
+ "@radix-ui/react-progress": "^1.1.8",
44
+ "@radix-ui/react-separator": "^1.1.8",
45
+ "@radix-ui/react-slot": "^1.2.4",
46
+ "@radix-ui/react-tooltip": "^1.2.8",
47
+ "class-variance-authority": "^0.7.1",
48
+ "clsx": "^2.1.1",
49
+ "date-fns": "^4.1.0",
50
+ "fluent-ffmpeg": "^2.1.2",
51
+ "formidable": "^3.5.4",
52
+ "googleapis": "^169.0.0",
53
+ "lucide-react": "^0.562.0",
54
+ "react-pdf": "^7.0.0",
55
+ "sharp": "^0.33.0",
56
+ "tailwind-merge": "^3.4.0",
57
+ "zod": "^3.22.0"
58
+ },
59
+ "devDependencies": {
60
+ "@semantic-release/changelog": "^6.0.3",
61
+ "@semantic-release/exec": "^7.1.0",
62
+ "@semantic-release/git": "^10.0.1",
63
+ "@types/fluent-ffmpeg": "^2.1.21",
64
+ "@types/formidable": "^3.4.6",
65
+ "@types/node": "^20.0.0",
66
+ "@types/react": "^18.0.0",
67
+ "@types/react-dom": "^18.0.0",
68
+ "@vitest/coverage-v8": "^4.0.16",
69
+ "eslint": "^8.0.0",
70
+ "jsdom": "^27.3.0",
71
+ "mongoose": ">=7.0.0",
72
+ "semantic-release": "^24.2.9",
73
+ "tsup": "^8.0.0",
74
+ "typescript": "^5.0.0",
75
+ "vitest": "^4.0.16"
76
+ },
77
+ "keywords": [
78
+ "next.js",
79
+ "file-storage",
80
+ "drive",
81
+ "upload",
82
+ "file-management"
83
+ ],
84
+ "license": "MIT",
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "https://github.com/your-org/next-file-manager"
88
+ }
89
+ }