@lacrous/omnicloud 0.1.1 → 0.2.1
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.
- package/dist/index.cjs +2049 -265
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1035 -121
- package/dist/index.d.ts +1035 -121
- package/dist/index.js +2036 -263
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../../shared/src/mime.ts","../../shared/src/index.ts","../../core/src/errors.ts","../../core/src/utils/hash.ts","../../core/src/storage/engine.ts","../../core/src/utils/filename.ts","../../core/src/services/file-service.ts","../../core/src/services/folder-service.ts","../../core/src/services/search-service.ts","../../telegram/src/client.ts","../../telegram/src/errors.ts","../../telegram/src/provider.ts","../../telegram/src/connection.ts"],"sourcesContent":["import type {\n DeleteFolderResultDTO,\n FileDTO,\n FolderDTO,\n SearchResultDTO,\n SessionInfo,\n StorageDTO,\n TelegramVerifyResponse,\n UserDTO,\n} from \"@omnicloud/shared\";\n\nexport class OmniCloudError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = \"OmniCloudError\";\n }\n}\n\nexport interface OmniCloudClientOptions {\n /** Base URL of an OmniCloud server. Defaults to same-origin (\"\" is kept as-is). */\n baseUrl?: string;\n /** Custom fetch implementation (e.g. for tests). */\n fetchImpl?: typeof fetch;\n}\n\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percent: number;\n}\n\nexport interface UploadOptions {\n folderId?: string | null;\n onProgress?: (progress: UploadProgress) => void;\n}\n\nexport interface UploadInput {\n /** File content: a File/Blob in the browser, or a Buffer/Uint8Array in Node. */\n data: Blob | Uint8Array;\n name: string;\n}\n\n/**\n * HTTP client for an OmniCloud server.\n *\n * Works in browsers (cookie sessions, upload progress via XHR) and Node.js\n * (fetch fallback, cookie forwarding is the caller's responsibility).\n */\nexport class OmniCloudClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: OmniCloudClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? \"\").replace(/\\/$/, \"\");\n this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);\n }\n\n // ── Authentication ───────────────────────────────────────────────────────\n\n async me(): Promise<SessionInfo> {\n return this.request(\"GET\", \"/api/auth/me\");\n }\n\n async startTelegramLogin(phone: string): Promise<void> {\n await this.request(\"POST\", \"/api/auth/telegram/start\", { phone });\n }\n\n async verifyTelegramCode(phone: string, code: string): Promise<TelegramVerifyResponse> {\n return this.request(\"POST\", \"/api/auth/telegram/verify\", { phone, code });\n }\n\n async submitTelegramPassword(\n phone: string,\n password: string,\n ): Promise<{ status: \"ok\"; user: UserDTO }> {\n return this.request(\"POST\", \"/api/auth/telegram/password\", { phone, password });\n }\n\n async ensureStorage(): Promise<StorageDTO> {\n const { storage } = await this.request<{ storage: StorageDTO }>(\"POST\", \"/api/storage/ensure\");\n return storage;\n }\n\n async logout(): Promise<void> {\n await this.request(\"POST\", \"/api/auth/logout\");\n }\n\n // ── Folders ──────────────────────────────────────────────────────────────\n\n async listFolders(parentId: string | null): Promise<FolderDTO[]> {\n const { folders } = await this.request<{ folders: FolderDTO[] }>(\n \"GET\",\n `/api/folders${parentId ? `?parentId=${encodeURIComponent(parentId)}` : \"\"}`,\n );\n return folders;\n }\n\n async folderTree(): Promise<FolderDTO[]> {\n const { folders } = await this.request<{ folders: FolderDTO[] }>(\"GET\", \"/api/folders/tree\");\n return folders;\n }\n\n async createFolder(name: string, parentId: string | null = null): Promise<FolderDTO> {\n const { folder } = await this.request<{ folder: FolderDTO }>(\"POST\", \"/api/folders\", {\n name,\n parentId,\n });\n return folder;\n }\n\n async renameFolder(id: string, name: string): Promise<FolderDTO> {\n const { folder } = await this.request<{ folder: FolderDTO }>(\"PATCH\", `/api/folders/${id}`, {\n name,\n });\n return folder;\n }\n\n async moveFolder(id: string, parentId: string | null): Promise<FolderDTO> {\n const { folder } = await this.request<{ folder: FolderDTO }>(\n \"POST\",\n `/api/folders/${id}/move`,\n { parentId },\n );\n return folder;\n }\n\n async deleteFolder(id: string): Promise<DeleteFolderResultDTO> {\n return this.request(\"DELETE\", `/api/folders/${id}`);\n }\n\n // ── Files ────────────────────────────────────────────────────────────────\n\n async listFiles(folderId: string | null): Promise<FileDTO[]> {\n const { files } = await this.request<{ files: FileDTO[] }>(\n \"GET\",\n `/api/files${folderId ? `?folderId=${encodeURIComponent(folderId)}` : \"\"}`,\n );\n return files;\n }\n\n async getFile(id: string): Promise<FileDTO> {\n const { file } = await this.request<{ file: FileDTO }>(\"GET\", `/api/files/${id}`);\n return file;\n }\n\n async renameFile(id: string, name: string): Promise<FileDTO> {\n const { file } = await this.request<{ file: FileDTO }>(\"PATCH\", `/api/files/${id}`, { name });\n return file;\n }\n\n async moveFile(id: string, folderId: string | null): Promise<FileDTO> {\n const { file } = await this.request<{ file: FileDTO }>(\"POST\", `/api/files/${id}/move`, {\n folderId,\n });\n return file;\n }\n\n async deleteFile(id: string): Promise<void> {\n await this.request(\"DELETE\", `/api/files/${id}`);\n }\n\n /** URL of the download endpoint (usable as href for direct downloads). */\n fileDownloadUrl(id: string): string {\n return `${this.baseUrl}/api/files/${id}/download`;\n }\n\n /**\n * Uploads a file. Uses XMLHttpRequest when available so `onProgress`\n * works (browsers); falls back to fetch without progress in Node.\n */\n async uploadFile(input: UploadInput, options: UploadOptions = {}): Promise<FileDTO> {\n const form = new FormData();\n const blob = input.data instanceof Blob ? input.data : new Blob([new Uint8Array(input.data)]);\n form.append(\"file\", blob, input.name);\n if (options.folderId) form.append(\"folderId\", options.folderId);\n\n if (typeof XMLHttpRequest === \"function\") {\n return this.uploadViaXhr(form, options.onProgress);\n }\n return this.request(\"POST\", \"/api/files\", form);\n }\n\n async search(query: string): Promise<SearchResultDTO> {\n return this.request(\"GET\", `/api/search?q=${encodeURIComponent(query)}`);\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private uploadViaXhr(\n form: FormData,\n onProgress?: (progress: UploadProgress) => void,\n ): Promise<FileDTO> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open(\"POST\", `${this.baseUrl}/api/files`);\n xhr.responseType = \"json\";\n xhr.withCredentials = true;\n xhr.upload.onprogress = (event) => {\n if (!onProgress) return;\n onProgress({\n loaded: event.loaded,\n total: event.total,\n percent: event.total > 0 ? Math.round((event.loaded / event.total) * 100) : 0,\n });\n };\n xhr.onload = () => {\n const body = xhr.response as {\n file?: FileDTO;\n error?: { code: string; message: string };\n } | null;\n if (xhr.status >= 200 && xhr.status < 300 && body?.file) {\n resolve(body.file);\n return;\n }\n reject(\n new OmniCloudError(\n xhr.status,\n body?.error?.code ?? \"INTERNAL_ERROR\",\n body?.error?.message ?? `Upload failed (${xhr.status})`,\n ),\n );\n };\n xhr.onerror = () =>\n reject(new OmniCloudError(0, \"NETWORK_ERROR\", \"Network error during upload\"));\n xhr.send(form);\n });\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const isJsonBody = body !== undefined && !(body instanceof FormData);\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: isJsonBody ? { \"Content-Type\": \"application/json\" } : undefined,\n body: body === undefined ? undefined : isJsonBody ? JSON.stringify(body) : (body as FormData),\n credentials: \"include\",\n });\n\n if (response.status === 204) return undefined as T;\n\n const data: unknown = await response.json().catch(() => null);\n if (!response.ok) {\n const error = (data as { error?: { code: string; message: string } } | null)?.error;\n throw new OmniCloudError(\n response.status,\n error?.code ?? \"INTERNAL_ERROR\",\n error?.message ?? `Request failed (${response.status})`,\n );\n }\n return data as T;\n }\n}\n","/**\n * Filename-extension based MIME type detection.\n *\n * MIME types supplied by clients are never trusted: the API derives the MIME\n * type from the filename extension server-side. Shared so the web UI can use\n * the same logic for icons.\n */\n\nconst MIME_BY_EXTENSION: Record<string, string> = {\n // Documents\n pdf: \"application/pdf\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n odt: \"application/vnd.oasis.opendocument.text\",\n ods: \"application/vnd.oasis.opendocument.spreadsheet\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n csv: \"text/csv\",\n rtf: \"application/rtf\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n xml: \"application/xml\",\n yaml: \"application/yaml\",\n yml: \"application/yaml\",\n json: \"application/json\",\n epub: \"application/epub+zip\",\n\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n avif: \"image/avif\",\n heic: \"image/heic\",\n\n // Audio\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n flac: \"audio/flac\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n opus: \"audio/opus\",\n\n // Video\n mp4: \"video/mp4\",\n mkv: \"video/x-matroska\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mov: \"video/quicktime\",\n wmv: \"video/x-ms-wmv\",\n flv: \"video/x-flv\",\n\n // Archives\n zip: \"application/zip\",\n rar: \"application/vnd.rar\",\n \"7z\": \"application/x-7z-compressed\",\n tar: \"application/x-tar\",\n gz: \"application/gzip\",\n bz2: \"application/x-bzip2\",\n xz: \"application/x-xz\",\n\n // Other\n apk: \"application/vnd.android.package-archive\",\n exe: \"application/x-msdownload\",\n dmg: \"application/x-apple-diskimage\",\n iso: \"application/x-iso9660-image\",\n bin: \"application/octet-stream\",\n};\n\nexport const DEFAULT_MIME_TYPE = \"application/octet-stream\";\n\n/** Returns the file extension without the leading dot, lowercased. */\nexport function fileExtension(filename: string): string {\n const base = filename.split(/[/\\\\]/).pop() ?? \"\";\n const dot = base.lastIndexOf(\".\");\n if (dot <= 0 || dot === base.length - 1) return \"\";\n return base.slice(dot + 1).toLowerCase();\n}\n\n/** Derives a MIME type from the filename extension. Never trusts client input. */\nexport function mimeFromFilename(filename: string): string {\n return MIME_BY_EXTENSION[fileExtension(filename)] ?? DEFAULT_MIME_TYPE;\n}\n\nexport type FileCategory =\n \"image\" | \"video\" | \"audio\" | \"pdf\" | \"archive\" | \"document\" | \"text\" | \"other\";\n\nconst PREFIXED_CATEGORIES: Array<[string, FileCategory]> = [\n [\"image/\", \"image\"],\n [\"video/\", \"video\"],\n [\"audio/\", \"audio\"],\n [\"application/pdf\", \"pdf\"],\n [\"application/zip\", \"archive\"],\n [\"application/x-tar\", \"archive\"],\n [\"application/gzip\", \"archive\"],\n [\"application/x-7z-compressed\", \"archive\"],\n [\"application/vnd.rar\", \"archive\"],\n [\"application/x-bzip2\", \"archive\"],\n [\"application/x-xz\", \"archive\"],\n [\"text/\", \"text\"],\n [\"application/json\", \"text\"],\n [\"application/xml\", \"text\"],\n [\"application/yaml\", \"text\"],\n [\"application/javascript\", \"text\"],\n [\"text/markdown\", \"text\"],\n];\n\nconst OFFICE_MIMES = new Set([\n \"application/msword\",\n \"application/vnd.ms-excel\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"application/vnd.oasis.opendocument.text\",\n \"application/vnd.oasis.opendocument.spreadsheet\",\n \"application/rtf\",\n \"application/epub+zip\",\n]);\n\n/** Coarse category used for icon selection in the UI. */\nexport function fileCategory(mimeType: string): FileCategory {\n for (const [prefix, category] of PREFIXED_CATEGORIES) {\n if (mimeType.startsWith(prefix)) return category;\n }\n if (OFFICE_MIMES.has(mimeType)) return \"document\";\n return \"other\";\n}\n","/**\n * Data transfer objects and constants shared between the API, the web app,\n * and the @lacrous/omnicloud SDK.\n *\n * Telegram identifiers that may exceed JavaScript's safe integer range are\n * transported as strings across the API boundary.\n */\n\nexport interface UserDTO {\n id: string;\n username: string | null;\n firstName: string | null;\n lastName: string | null;\n}\n\nexport interface StorageDTO {\n id: string;\n provider: string;\n title: string;\n createdAt: string;\n}\n\n/** Returned by GET /api/auth/me. `user` is null when not signed in. */\nexport interface SessionInfo {\n user: UserDTO | null;\n storage: StorageDTO | null;\n}\n\nexport interface FileDTO {\n id: string;\n name: string;\n size: number;\n mimeType: string;\n sha256: string;\n folderId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface FolderDTO {\n id: string;\n name: string;\n parentId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SearchResultDTO {\n files: FileDTO[];\n folders: FolderDTO[];\n}\n\nexport interface DeleteFolderResultDTO {\n deletedFolders: number;\n deletedFiles: number;\n}\n\n/** POST /api/auth/telegram/verify response. */\nexport type TelegramVerifyResponse =\n { status: \"ok\"; user: UserDTO } | { status: \"password_required\" };\n\n/** Stable machine-readable error codes returned in API error bodies. */\nexport const ERROR_CODES = {\n VALIDATION: \"VALIDATION_ERROR\",\n UNAUTHENTICATED: \"UNAUTHENTICATED\",\n FORBIDDEN: \"FORBIDDEN\",\n NOT_FOUND: \"NOT_FOUND\",\n CONFLICT: \"CONFLICT\",\n PAYLOAD_TOO_LARGE: \"PAYLOAD_TOO_LARGE\",\n RATE_LIMITED: \"RATE_LIMITED\",\n STORAGE_NOT_INITIALIZED: \"STORAGE_NOT_INITIALIZED\",\n TELEGRAM_ERROR: \"TELEGRAM_ERROR\",\n INTERNAL: \"INTERNAL_ERROR\",\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\nexport interface ApiErrorBody {\n error: {\n code: ErrorCode;\n message: string;\n details?: unknown;\n };\n}\n\nexport const SESSION_COOKIE = \"omnicloud_session\";\n\nexport * from \"./mime\";\n","import { ERROR_CODES } from \"@omnicloud/shared\";\nimport type { ErrorCode } from \"@omnicloud/shared\";\n\n/**\n * Base class for all domain errors. The API maps these to structured HTTP\n * responses; everything else becomes a 500.\n */\nexport class DomainError extends Error {\n readonly code: ErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ErrorCode, status: number, message: string, details?: unknown) {\n super(message);\n this.name = new.target.name;\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport class ValidationError extends DomainError {\n constructor(message: string, details?: unknown) {\n super(ERROR_CODES.VALIDATION, 400, message, details);\n }\n}\n\nexport class UnauthorizedError extends DomainError {\n constructor(message = \"Authentication required\") {\n super(ERROR_CODES.UNAUTHENTICATED, 401, message);\n }\n}\n\nexport class ForbiddenError extends DomainError {\n constructor(message = \"You do not have access to this resource\") {\n super(ERROR_CODES.FORBIDDEN, 403, message);\n }\n}\n\n/** Used for missing resources as well as resources owned by someone else, to avoid leaking existence. */\nexport class NotFoundError extends DomainError {\n constructor(message = \"Resource not found\") {\n super(ERROR_CODES.NOT_FOUND, 404, message);\n }\n}\n\nexport class ConflictError extends DomainError {\n constructor(message: string) {\n super(ERROR_CODES.CONFLICT, 409, message);\n }\n}\n\nexport class PayloadTooLargeError extends DomainError {\n constructor(message = \"File is too large\") {\n super(ERROR_CODES.PAYLOAD_TOO_LARGE, 413, message);\n }\n}\n\nexport class RateLimitedError extends DomainError {\n constructor(message = \"Too many requests, please retry later\") {\n super(ERROR_CODES.RATE_LIMITED, 429, message);\n }\n}\n\n/** The user has no storage backend set up yet. */\nexport class StorageNotInitializedError extends DomainError {\n constructor() {\n super(ERROR_CODES.STORAGE_NOT_INITIALIZED, 409, \"Storage is not initialized for this account\");\n }\n}\n\n/** The underlying storage backend (e.g. Telegram) failed. */\nexport class StorageProviderError extends DomainError {\n constructor(message: string, details?: unknown) {\n super(ERROR_CODES.TELEGRAM_ERROR, 502, message, details);\n }\n}\n","import { createHash } from \"node:crypto\";\n\nexport function sha256Hex(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n","import type { StoredObject, StoredRef, StorageProvider, StorageUploadInput } from \"./provider\";\nimport { StorageProviderError } from \"../errors\";\n\nexport { sha256Hex } from \"../utils/hash\";\nimport { sha256Hex } from \"../utils/hash\";\n\nexport interface EngineUploadResult {\n stored: StoredObject;\n sha256: string;\n size: number;\n}\n\n/**\n * Sits between the application and the raw StorageProvider and adds the\n * cross-cutting guarantees: SHA-256 integrity and consistent error mapping.\n *\n * The active provider is chosen per user by the host application (each user\n * has their own Telegram storage channel).\n */\nexport class StorageEngine {\n constructor(private readonly provider: StorageProvider) {}\n\n get providerName(): string {\n return this.provider.name;\n }\n\n /** Uploads an object and computes its checksum in one step. */\n async upload(input: StorageUploadInput): Promise<EngineUploadResult> {\n const sha256 = sha256Hex(input.data);\n let stored: StoredObject;\n try {\n stored = await this.provider.put(input);\n } catch (error) {\n throw mapProviderError(\"Upload failed\", error);\n }\n return { stored, sha256, size: input.data.byteLength };\n }\n\n async download(ref: StoredRef): Promise<Buffer> {\n try {\n return await this.provider.get(ref);\n } catch (error) {\n throw mapProviderError(\"Download failed\", error);\n }\n }\n\n async remove(ref: StoredRef): Promise<void> {\n try {\n await this.provider.delete(ref);\n } catch (error) {\n throw mapProviderError(\"Delete failed\", error);\n }\n }\n\n async verifyIntegrity(data: Buffer, expectedSha256: string): Promise<boolean> {\n return sha256Hex(data) === expectedSha256;\n }\n}\n\nexport function mapProviderError(message: string, error: unknown): StorageProviderError {\n if (error instanceof StorageProviderError) return error;\n const detail = error instanceof Error ? error.message : String(error);\n return new StorageProviderError(detail ? `${message}: ${detail}` : message, error);\n}\n","/**\n * Sanitizes a client-supplied filename into a safe, single path component.\n * Returns an empty string when nothing usable remains.\n */\nexport function sanitizeFileName(name: string): string {\n // Drop any path components — prevents path traversal.\n const base = name.split(/[/\\\\]/).pop() ?? name;\n // Strip control characters and characters that are unsafe across filesystems.\n const cleaned = base\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f]/g, \"\")\n .replace(/[<>:\"|?*]/g, \"_\")\n .trim();\n if (!cleaned || cleaned === \".\" || cleaned === \"..\") return \"\";\n return cleaned.slice(0, 255);\n}\n","import { NotFoundError, ValidationError } from \"../errors\";\nimport { sanitizeFileName } from \"../utils/filename\";\nimport { mimeFromFilename } from \"@omnicloud/shared\";\nimport type { FileRecord } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\nimport type { StorageEngine } from \"../storage/engine\";\n\nexport interface FileDownload {\n record: FileRecord;\n data: Buffer;\n integrityVerified: boolean;\n}\n\nexport type EngineResolver = (userId: string) => Promise<StorageEngine>;\n\n/**\n * All file metadata operations. Every method verifies ownership: records\n * belonging to other users are reported as missing.\n */\nexport class FileService {\n constructor(\n private readonly files: FileRepository,\n private readonly folders: FolderRepository,\n private readonly engineFor: EngineResolver,\n ) {}\n\n async upload(\n userId: string,\n input: { folderId: string | null; name: string; data: Buffer },\n ): Promise<FileRecord> {\n const name = sanitizeFileName(input.name);\n if (!name) throw new ValidationError(\"Invalid file name\");\n\n if (input.folderId !== null) {\n const folder = await this.folders.findById(input.folderId);\n if (!folder || folder.userId !== userId) throw new NotFoundError(\"Folder not found\");\n }\n\n const mimeType = mimeFromFilename(name);\n const engine = await this.engineFor(userId);\n\n // Upload first; metadata is only persisted once the provider succeeds.\n const { stored, sha256, size } = await engine.upload({ name, mimeType, data: input.data });\n\n return this.files.create({\n userId,\n folderId: input.folderId,\n name,\n size,\n mimeType,\n sha256,\n telegramMessageId: Number(stored.messageId),\n });\n }\n\n async list(userId: string, folderId: string | null): Promise<FileRecord[]> {\n if (folderId !== null) {\n const folder = await this.folders.findById(folderId);\n if (!folder || folder.userId !== userId) throw new NotFoundError(\"Folder not found\");\n }\n return this.files.listByFolder(userId, folderId);\n }\n\n async get(userId: string, id: string): Promise<FileRecord> {\n const record = await this.files.findById(id);\n if (!record || record.userId !== userId) throw new NotFoundError(\"File not found\");\n return record;\n }\n\n async rename(userId: string, id: string, name: string): Promise<FileRecord> {\n await this.get(userId, id);\n const clean = sanitizeFileName(name);\n if (!clean) throw new ValidationError(\"Invalid file name\");\n return this.files.update(id, { name: clean });\n }\n\n async move(userId: string, id: string, folderId: string | null): Promise<FileRecord> {\n await this.get(userId, id);\n if (folderId !== null) {\n const folder = await this.folders.findById(folderId);\n if (!folder || folder.userId !== userId) throw new NotFoundError(\"Folder not found\");\n }\n return this.files.update(id, { folderId });\n }\n\n async delete(userId: string, id: string): Promise<void> {\n const record = await this.get(userId, id);\n const engine = await this.engineFor(userId);\n try {\n await engine.remove({ messageId: String(record.telegramMessageId) });\n } catch {\n // The remote object is already gone or unreachable; still remove the\n // metadata so the file does not linger in the user's drive.\n }\n await this.files.deleteMany([record.id]);\n }\n\n async download(userId: string, id: string): Promise<FileDownload> {\n const record = await this.get(userId, id);\n const engine = await this.engineFor(userId);\n const data = await engine.download({ messageId: String(record.telegramMessageId) });\n const integrityVerified = await engine.verifyIntegrity(data, record.sha256);\n return { record, data, integrityVerified };\n }\n}\n","import { NotFoundError, ValidationError } from \"../errors\";\nimport { sanitizeFileName } from \"../utils/filename\";\nimport type { FolderRecord } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\nimport type { EngineResolver } from \"./file-service\";\n\nexport interface FolderDeletionResult {\n deletedFolders: number;\n deletedFiles: number;\n}\n\n/**\n * Folder deletion semantics (v0.1): deleting a folder recursively deletes\n * all descendant folders and the files inside them, including the remote\n * storage objects. Files directly inside the deleted hierarchy are removed,\n * not relocated.\n */\nexport class FolderService {\n constructor(\n private readonly folders: FolderRepository,\n private readonly files: FileRepository,\n private readonly engineFor: EngineResolver,\n ) {}\n\n async create(\n userId: string,\n input: { name: string; parentId: string | null },\n ): Promise<FolderRecord> {\n const name = sanitizeFileName(input.name);\n if (!name) throw new ValidationError(\"Invalid folder name\");\n if (input.parentId !== null) {\n const parent = await this.folders.findById(input.parentId);\n if (!parent || parent.userId !== userId) throw new NotFoundError(\"Folder not found\");\n }\n return this.folders.create({ userId, parentId: input.parentId, name });\n }\n\n async listChildren(userId: string, parentId: string | null): Promise<FolderRecord[]> {\n if (parentId !== null) {\n const parent = await this.folders.findById(parentId);\n if (!parent || parent.userId !== userId) throw new NotFoundError(\"Folder not found\");\n }\n return this.folders.listChildren(userId, parentId);\n }\n\n async tree(userId: string): Promise<FolderRecord[]> {\n return this.folders.listByUser(userId);\n }\n\n async get(userId: string, id: string): Promise<FolderRecord> {\n const record = await this.folders.findById(id);\n if (!record || record.userId !== userId) throw new NotFoundError(\"Folder not found\");\n return record;\n }\n\n async rename(userId: string, id: string, name: string): Promise<FolderRecord> {\n await this.get(userId, id);\n const clean = sanitizeFileName(name);\n if (!clean) throw new ValidationError(\"Invalid folder name\");\n return this.folders.update(id, { name: clean });\n }\n\n async move(userId: string, id: string, newParentId: string | null): Promise<FolderRecord> {\n await this.get(userId, id);\n\n if (newParentId === id) throw new ValidationError(\"Cannot move a folder into itself\");\n\n if (newParentId !== null) {\n // Walk up from the new parent; hitting the folder being moved means it\n // would become its own ancestor.\n let cursor: string | null = newParentId;\n while (cursor !== null) {\n if (cursor === id) {\n throw new ValidationError(\"Cannot move a folder into one of its subfolders\");\n }\n const parent = await this.folders.findById(cursor);\n if (!parent || parent.userId !== userId) throw new NotFoundError(\"Folder not found\");\n cursor = parent.parentId;\n }\n }\n\n return this.folders.update(id, { parentId: newParentId });\n }\n\n async delete(userId: string, id: string): Promise<FolderDeletionResult> {\n await this.get(userId, id);\n\n const all = await this.folders.listByUser(userId);\n const childrenOf = new Map<string | null, string[]>();\n for (const folder of all) {\n const list = childrenOf.get(folder.parentId) ?? [];\n list.push(folder.id);\n childrenOf.set(folder.parentId, list);\n }\n\n // Collect the folder and all descendants (BFS).\n const doomed: string[] = [id];\n const queue = [id];\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const child of childrenOf.get(current) ?? []) {\n doomed.push(child);\n queue.push(child);\n }\n }\n const doomedSet = new Set(doomed);\n\n const userFiles = await this.files.listByUser(userId);\n const affectedFiles = userFiles.filter((f) => f.folderId !== null && doomedSet.has(f.folderId));\n\n // Best effort: remove remote objects first so metadata deletion below\n // always succeeds. A leftover remote object is harmless; a stuck folder\n // is not.\n const engine = await this.engineFor(userId);\n for (const file of affectedFiles) {\n try {\n await engine.remove({ messageId: String(file.telegramMessageId) });\n } catch {\n // Ignore individual failures — metadata cleanup must proceed.\n }\n }\n\n await this.files.deleteMany(affectedFiles.map((f) => f.id));\n await this.folders.deleteMany(doomed);\n\n return { deletedFolders: doomed.length, deletedFiles: affectedFiles.length };\n }\n}\n","import { ValidationError } from \"../errors\";\nimport type { FileRecord, FolderRecord } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\n\nconst MAX_QUERY_LENGTH = 100;\n\n/** Metadata-based search over file and folder names (PostgreSQL-backed). */\nexport class SearchService {\n constructor(\n private readonly files: FileRepository,\n private readonly folders: FolderRepository,\n ) {}\n\n async search(\n userId: string,\n query: string,\n limit = 25,\n ): Promise<{ files: FileRecord[]; folders: FolderRecord[] }> {\n const q = query.trim();\n if (!q) throw new ValidationError(\"Search query must not be empty\");\n if (q.length > MAX_QUERY_LENGTH) {\n throw new ValidationError(`Search query must be at most ${MAX_QUERY_LENGTH} characters`);\n }\n const [fileResults, folderResults] = await Promise.all([\n this.files.searchByName(userId, q, limit),\n this.folders.searchByName(userId, q, limit),\n ]);\n return { files: fileResults, folders: folderResults };\n }\n}\n","import { Api, TelegramClient } from \"telegram\";\nimport { StringSession } from \"telegram/sessions/index.js\";\nimport { StorageProviderError, UnauthorizedError } from \"@omnicloud/core\";\nimport type { SessionRepository } from \"@omnicloud/core\";\nimport { mapTelegramError } from \"./errors\";\n\nexport interface TelegramCredentials {\n apiId: number;\n apiHash: string;\n}\n\n/**\n * Keeps one connected TelegramClient per authenticated user. Clients are\n * restored from the persisted MTProto session string and cached for the\n * lifetime of the process.\n */\nexport class TelegramClientManager {\n private clients = new Map<string, Promise<TelegramClient>>();\n\n constructor(\n private readonly credentials: TelegramCredentials,\n private readonly sessions: SessionRepository,\n ) {}\n\n async getForUser(userId: string): Promise<TelegramClient> {\n const cached = this.clients.get(userId);\n if (cached) {\n try {\n return await cached;\n } catch {\n this.clients.delete(userId); // stale entry — reconnect below\n }\n }\n\n const session = await this.sessions.get(userId);\n if (!session) {\n throw new UnauthorizedError(\"Telegram account is not connected\");\n }\n\n const promise = this.connect(session.stringSession);\n this.clients.set(userId, promise);\n try {\n return await promise;\n } catch (error) {\n this.clients.delete(userId);\n throw mapTelegramError(error, \"Could not connect to Telegram\");\n }\n }\n\n /** Registers an already-authenticated client (e.g. from the login flow). */\n cacheClient(userId: string, client: TelegramClient): void {\n this.clients.set(userId, Promise.resolve(client));\n }\n\n async disconnectAll(): Promise<void> {\n for (const [userId, promise] of this.clients) {\n this.clients.delete(userId);\n try {\n const client = await promise;\n await client.disconnect();\n } catch {\n // best effort on shutdown\n }\n }\n }\n\n private async connect(sessionString: string): Promise<TelegramClient> {\n const client = new TelegramClient(\n new StringSession(sessionString),\n this.credentials.apiId,\n this.credentials.apiHash,\n { connectionRetries: 3 },\n );\n await client.connect();\n return client;\n }\n}\n\nexport function newTelegramClient(credentials: TelegramCredentials): TelegramClient {\n return new TelegramClient(new StringSession(\"\"), credentials.apiId, credentials.apiHash, {\n connectionRetries: 3,\n });\n}\n\n/**\n * Extracts the freshly created broadcast channel from a\n * channels.CreateChannel response.\n */\nexport function extractCreatedChannel(result: Api.TypeUpdates): Api.Channel {\n const chats =\n result instanceof Api.Updates || result instanceof Api.UpdatesCombined ? result.chats : [];\n for (const chat of chats) {\n if (chat instanceof Api.Channel) return chat;\n }\n throw new StorageProviderError(\"Telegram did not return the created storage channel\");\n}\n","import { RPCError } from \"telegram/errors/index.js\";\nimport {\n DomainError,\n ForbiddenError,\n RateLimitedError,\n StorageNotInitializedError,\n StorageProviderError,\n UnauthorizedError,\n ValidationError,\n} from \"@omnicloud/core\";\n\nexport function isTelegramRpcError(error: unknown): error is RPCError {\n return error instanceof RPCError;\n}\n\n/** True when the error signals that 2FA password auth is required. */\nexport function isPasswordRequiredError(error: unknown): boolean {\n return isTelegramRpcError(error) && error.errorMessage === \"SESSION_PASSWORD_NEEDED\";\n}\n\n/**\n * Maps a Telegram (GramJS) failure to a structured domain error with an\n * appropriate HTTP status and client-facing message.\n */\nexport function mapTelegramError(error: unknown, fallbackMessage: string): DomainError {\n if (error instanceof DomainError) return error;\n\n if (isTelegramRpcError(error)) {\n const msg = error.errorMessage ?? \"\";\n\n if (msg.startsWith(\"FLOOD_WAIT_\")) {\n const seconds = Number(msg.slice(\"FLOOD_WAIT_\".length)) || 60;\n return new RateLimitedError(`Telegram rate limit reached — retry in ${seconds} seconds`);\n }\n\n switch (msg) {\n case \"PHONE_NUMBER_INVALID\":\n return new ValidationError(\"This phone number is not valid\");\n case \"PHONE_NUMBER_UNOCCUPIED\":\n return new ValidationError(\"No Telegram account exists for this phone number\");\n case \"PHONE_CODE_INVALID\":\n return new ValidationError(\"Invalid confirmation code\");\n case \"PHONE_CODE_EXPIRED\":\n return new ValidationError(\"The confirmation code has expired — request a new one\");\n case \"PHONE_CODE_EMPTY\":\n return new ValidationError(\"Please enter the confirmation code\");\n case \"SESSION_PASSWORD_NEEDED\":\n return new UnauthorizedError(\"Two-factor authentication is enabled on this account\");\n case \"PASSWORD_HASH_INVALID\":\n return new ValidationError(\"Incorrect two-factor password\");\n case \"PHONE_NUMBER_BANNED\":\n return new ForbiddenError(\"This phone number is banned from Telegram\");\n case \"AUTH_KEY_UNREGISTERED\":\n case \"SESSION_REVOKED\":\n case \"USER_DEACTIVATED\":\n return new UnauthorizedError(\"The Telegram session is no longer valid — please reconnect\");\n case \"CHANNEL_PRIVATE\":\n case \"CHAT_ID_INVALID\":\n return new StorageNotInitializedError();\n default:\n return new StorageProviderError(fallbackMessage, {\n code: error.code,\n telegramError: msg,\n });\n }\n }\n\n return new StorageProviderError(\n error instanceof Error ? `${fallbackMessage}: ${error.message}` : fallbackMessage,\n error,\n );\n}\n","import { Api, TelegramClient } from \"telegram\";\nimport bigInt from \"big-integer\";\nimport { CustomFile } from \"telegram/client/uploads.js\";\nimport {\n StorageProviderError,\n mapProviderError,\n type StorageProvider,\n type StorageUploadInput,\n type StoredObject,\n type StoredRef,\n} from \"@omnicloud/core\";\nimport { mapTelegramError } from \"./errors\";\n\nexport interface TelegramStorageOptions {\n /** Raw channel id of the user's private storage channel. */\n chatId: string;\n /** Channel access hash required to build the input peer. */\n accessHash: string;\n}\n\n/**\n * StorageProvider implementation backed by the user's private Telegram\n * channel. Files are sent as force-downloaded documents (no compression),\n * so every upload is a document message in the channel.\n */\nexport class TelegramStorageProvider implements StorageProvider {\n readonly name = \"telegram\";\n private readonly peer: Api.InputPeerChannel;\n\n constructor(\n private readonly client: TelegramClient,\n options: TelegramStorageOptions,\n ) {\n this.peer = new Api.InputPeerChannel({\n channelId: bigInt(options.chatId),\n accessHash: bigInt(options.accessHash),\n });\n }\n\n async put(input: StorageUploadInput): Promise<StoredObject> {\n try {\n const file = new CustomFile(input.name, input.data.byteLength, \"\", input.data);\n const message = await this.client.sendFile(this.peer, {\n file,\n forceDocument: true,\n workers: 1,\n });\n if (!(message instanceof Api.Message)) {\n throw new StorageProviderError(\"Telegram returned an unexpected upload response\");\n }\n return {\n messageId: message.id.toString(),\n name: input.name,\n size: input.data.byteLength,\n mimeType: input.mimeType,\n };\n } catch (error) {\n throw mapTelegramError(error, \"Telegram upload failed\");\n }\n }\n\n async get(ref: StoredRef): Promise<Buffer> {\n let data: Buffer | string | undefined;\n try {\n const message = await this.getMessage(ref.messageId);\n if (!message?.media) {\n throw new StorageProviderError(\"The stored file is missing from Telegram\");\n }\n data = await this.client.downloadMedia(message, {});\n } catch (error) {\n if (error instanceof StorageProviderError) throw error;\n throw mapTelegramError(error, \"Telegram download failed\");\n }\n if (!data || typeof data === \"string\") {\n throw new StorageProviderError(\"Telegram did not return file content\");\n }\n return Buffer.from(data);\n }\n\n async delete(ref: StoredRef): Promise<void> {\n try {\n await this.client.deleteMessages(this.peer, [Number(ref.messageId)], { revoke: true });\n } catch (error) {\n throw mapTelegramError(error, \"Telegram delete failed\");\n }\n }\n\n async exists(ref: StoredRef): Promise<boolean> {\n try {\n return (await this.stat(ref)) !== null;\n } catch (error) {\n throw mapTelegramError(error, \"Telegram lookup failed\");\n }\n }\n\n async stat(ref: StoredRef): Promise<StoredObject | null> {\n let message: Api.Message | undefined;\n try {\n message = await this.getMessage(ref.messageId);\n } catch (error) {\n throw mapTelegramError(error, \"Telegram lookup failed\");\n }\n if (!message?.media) return null;\n\n const document = message.document;\n if (!(document instanceof Api.Document)) return null;\n\n let fileName: string | undefined;\n for (const attr of document.attributes) {\n if (attr instanceof Api.DocumentAttributeFilename) {\n fileName = attr.fileName;\n break;\n }\n }\n return {\n messageId: ref.messageId,\n name: fileName ?? \"file\",\n size: Number(document.size.toString()),\n mimeType: document.mimeType,\n };\n }\n\n private async getMessage(id: string): Promise<Api.Message | undefined> {\n const messages = await this.client.getMessages(this.peer, { ids: [Number(id)] });\n const first = messages[0];\n return first instanceof Api.Message ? first : undefined;\n }\n}\n\n// Re-exported so hosts can wrap provider errors consistently.\nexport { mapProviderError };\n","import { Api, TelegramClient } from \"telegram\";\nimport { computeCheck } from \"telegram/Password.js\";\nimport {\n NotFoundError,\n StorageProviderError,\n ValidationError,\n type Repos,\n type StorageRecord,\n type UserRecord,\n} from \"@omnicloud/core\";\nimport {\n extractCreatedChannel,\n newTelegramClient,\n type TelegramClientManager,\n type TelegramCredentials,\n} from \"./client\";\nimport { isPasswordRequiredError, mapTelegramError } from \"./errors\";\n\nexport const TELEGRAM_PROVIDER = \"telegram\";\n\n/**\n * A Telegram login in progress: a connected client waiting for the\n * confirmation code (and possibly the 2FA password) to be supplied.\n */\ninterface LoginFlow {\n phone: string;\n client: TelegramClient;\n phoneCodeHash: string;\n createdAt: number;\n}\n\nconst LOGIN_FLOW_TTL_MS = 5 * 60 * 1000;\n\nexport type VerifyCodeResult = { status: \"ok\"; user: UserRecord } | { status: \"password_required\" };\n\n/**\n * Orchestrates the full Telegram account connection flow:\n * phone → code (→ 2FA password) → session persisted → user upserted →\n * private storage channel created.\n */\nexport class TelegramConnectionService {\n private flows = new Map<string, LoginFlow>();\n\n constructor(\n private readonly repos: Repos,\n private readonly clientManager: TelegramClientManager,\n private readonly credentials: TelegramCredentials,\n ) {}\n\n /** Sends the Telegram confirmation code for the phone number. */\n async startLogin(phone: string): Promise<void> {\n this.sweepExpiredFlows();\n await this.cancelLogin(phone);\n\n const client = newTelegramClient(this.credentials);\n try {\n await client.connect();\n const { phoneCodeHash } = await client.sendCode(\n { apiId: this.credentials.apiId, apiHash: this.credentials.apiHash },\n phone,\n );\n this.flows.set(phone, { phone, client, phoneCodeHash, createdAt: Date.now() });\n } catch (error) {\n await this.disconnectQuietly(client);\n throw mapTelegramError(error, \"Could not send the Telegram confirmation code\");\n }\n }\n\n /** Verifies the confirmation code. Returns `password_required` for 2FA accounts. */\n async verifyCode(phone: string, code: string): Promise<VerifyCodeResult> {\n const flow = this.requireFlow(phone);\n try {\n await flow.client.invoke(\n new Api.auth.SignIn({\n phoneNumber: flow.phone,\n phoneCodeHash: flow.phoneCodeHash,\n phoneCode: code,\n }),\n );\n } catch (error) {\n if (isPasswordRequiredError(error)) {\n return { status: \"password_required\" };\n }\n throw mapTelegramError(error, \"Telegram sign-in failed\");\n }\n return this.finalizeLogin(flow);\n }\n\n /** Completes sign-in for accounts with two-factor authentication enabled. */\n async verifyPassword(phone: string, password: string): Promise<VerifyCodeResult> {\n const flow = this.requireFlow(phone);\n try {\n const passwordInfo = await flow.client.invoke(new Api.account.GetPassword());\n const check = await computeCheck(passwordInfo, password);\n await flow.client.invoke(new Api.auth.CheckPassword({ password: check }));\n } catch (error) {\n throw mapTelegramError(error, \"Telegram sign-in failed\");\n }\n return this.finalizeLogin(flow);\n }\n\n /** Aborts a pending login flow (disconnects the client). */\n async cancelLogin(phone: string): Promise<void> {\n const flow = this.flows.get(phone);\n if (flow) {\n this.flows.delete(phone);\n await this.disconnectQuietly(flow.client);\n }\n }\n\n /**\n * Ensures the user has a registered private storage channel, creating it\n * on first use. Idempotent.\n */\n async ensureStorage(userId: string): Promise<StorageRecord> {\n const existing = await this.repos.storages.findByUserAndProvider(userId, TELEGRAM_PROVIDER);\n if (existing) return existing;\n\n const client = await this.clientManager.getForUser(userId);\n return this.ensureStorageForClient(userId, client);\n }\n\n async ensureStorageForClient(userId: string, client: TelegramClient): Promise<StorageRecord> {\n const existing = await this.repos.storages.findByUserAndProvider(userId, TELEGRAM_PROVIDER);\n if (existing) return existing;\n\n let channel: Api.Channel;\n try {\n const result = await client.invoke(\n new Api.channels.CreateChannel({\n title: \"OmniCloud Storage\",\n about:\n \"Private storage channel created by OmniCloud (https://github.com/Lacrous/OmniCloud)\",\n megagroup: false,\n }),\n );\n channel = extractCreatedChannel(result);\n } catch (error) {\n throw mapTelegramError(error, \"Could not create the private storage channel\");\n }\n\n const accessHash = channel.accessHash?.toString();\n if (!accessHash) {\n throw new StorageProviderError(\"Telegram did not provide the channel access hash\");\n }\n\n return this.repos.storages.create({\n userId,\n provider: TELEGRAM_PROVIDER,\n title: channel.title ?? \"OmniCloud Storage\",\n telegramChatId: channel.id.toString(),\n telegramAccessHash: accessHash,\n });\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private requireFlow(phone: string): LoginFlow {\n const flow = this.flows.get(phone);\n if (!flow) {\n throw new NotFoundError(\"No pending Telegram login for this phone number\");\n }\n if (Date.now() - flow.createdAt > LOGIN_FLOW_TTL_MS) {\n this.flows.delete(phone);\n void this.disconnectQuietly(flow.client);\n throw new ValidationError(\"The login session expired — please start again\");\n }\n return flow;\n }\n\n private async finalizeLogin(flow: LoginFlow): Promise<VerifyCodeResult> {\n this.flows.delete(flow.phone);\n\n let stringSession: string;\n let user: UserRecord;\n try {\n stringSession = flow.client.session.save() as unknown as string;\n const me = await flow.client.getMe();\n if (!(me instanceof Api.User)) {\n throw new Error(\"Telegram did not return the authenticated user\");\n }\n user = await this.repos.users.upsertFromTelegram({\n telegramUserId: me.id.toString(),\n username: me.username ?? null,\n firstName: me.firstName ?? null,\n lastName: me.lastName ?? null,\n phone: me.phone ?? flow.phone,\n });\n } catch (error) {\n await this.disconnectQuietly(flow.client);\n throw mapTelegramError(error, \"Could not complete the Telegram connection\");\n }\n\n await this.repos.sessions.save(user.id, stringSession);\n await this.ensureStorageForClient(user.id, flow.client);\n this.clientManager.cacheClient(user.id, flow.client);\n\n return { status: \"ok\", user };\n }\n\n private sweepExpiredFlows(): void {\n const now = Date.now();\n for (const [phone, flow] of this.flows) {\n if (now - flow.createdAt > LOGIN_FLOW_TTL_MS) {\n this.flows.delete(phone);\n void this.disconnectQuietly(flow.client);\n }\n }\n }\n\n private async disconnectQuietly(client: TelegramClient): Promise<void> {\n try {\n await client.disconnect();\n } catch {\n // best effort\n }\n }\n}\n"],"mappings":";AAWO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YACkB,QACA,MAChB,SACA;AACA,UAAM,OAAO;AAJG;AACA;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EANkB;AAAA,EACA;AAMpB;AAgCO,IAAM,kBAAN,MAAsB;AAAA,EACV;AAAA,EACA;AAAA,EAEjB,YAAY,UAAkC,CAAC,GAAG;AAChD,SAAK,WAAW,QAAQ,WAAW,IAAI,QAAQ,OAAO,EAAE;AACxD,SAAK,YAAY,QAAQ,aAAa,MAAM,KAAK,UAAU;AAAA,EAC7D;AAAA;AAAA,EAIA,MAAM,KAA2B;AAC/B,WAAO,KAAK,QAAQ,OAAO,cAAc;AAAA,EAC3C;AAAA,EAEA,MAAM,mBAAmB,OAA8B;AACrD,UAAM,KAAK,QAAQ,QAAQ,4BAA4B,EAAE,MAAM,CAAC;AAAA,EAClE;AAAA,EAEA,MAAM,mBAAmB,OAAe,MAA+C;AACrF,WAAO,KAAK,QAAQ,QAAQ,6BAA6B,EAAE,OAAO,KAAK,CAAC;AAAA,EAC1E;AAAA,EAEA,MAAM,uBACJ,OACA,UAC0C;AAC1C,WAAO,KAAK,QAAQ,QAAQ,+BAA+B,EAAE,OAAO,SAAS,CAAC;AAAA,EAChF;AAAA,EAEA,MAAM,gBAAqC;AACzC,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,QAAiC,QAAQ,qBAAqB;AAC7F,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAwB;AAC5B,UAAM,KAAK,QAAQ,QAAQ,kBAAkB;AAAA,EAC/C;AAAA;AAAA,EAIA,MAAM,YAAY,UAA+C;AAC/D,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK;AAAA,MAC7B;AAAA,MACA,eAAe,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK,EAAE;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAmC;AACvC,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,QAAkC,OAAO,mBAAmB;AAC3F,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,MAAc,WAA0B,MAA0B;AACnF,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,QAA+B,QAAQ,gBAAgB;AAAA,MACnF;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,IAAY,MAAkC;AAC/D,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,QAA+B,SAAS,gBAAgB,EAAE,IAAI;AAAA,MAC1F;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,IAAY,UAA6C;AACxE,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK;AAAA,MAC5B;AAAA,MACA,gBAAgB,EAAE;AAAA,MAClB,EAAE,SAAS;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,IAA4C;AAC7D,WAAO,KAAK,QAAQ,UAAU,gBAAgB,EAAE,EAAE;AAAA,EACpD;AAAA;AAAA,EAIA,MAAM,UAAU,UAA6C;AAC3D,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA,aAAa,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK,EAAE;AAAA,IAC1E;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,IAA8B;AAC1C,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,QAA2B,OAAO,cAAc,EAAE,EAAE;AAChF,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,IAAY,MAAgC;AAC3D,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,QAA2B,SAAS,cAAc,EAAE,IAAI,EAAE,KAAK,CAAC;AAC5F,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,IAAY,UAA2C;AACpE,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,QAA2B,QAAQ,cAAc,EAAE,SAAS;AAAA,MACtF;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,IAA2B;AAC1C,UAAM,KAAK,QAAQ,UAAU,cAAc,EAAE,EAAE;AAAA,EACjD;AAAA;AAAA,EAGA,gBAAgB,IAAoB;AAClC,WAAO,GAAG,KAAK,OAAO,cAAc,EAAE;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,OAAoB,UAAyB,CAAC,GAAqB;AAClF,UAAM,OAAO,IAAI,SAAS;AAC1B,UAAM,OAAO,MAAM,gBAAgB,OAAO,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,WAAW,MAAM,IAAI,CAAC,CAAC;AAC5F,SAAK,OAAO,QAAQ,MAAM,MAAM,IAAI;AACpC,QAAI,QAAQ,SAAU,MAAK,OAAO,YAAY,QAAQ,QAAQ;AAE9D,QAAI,OAAO,mBAAmB,YAAY;AACxC,aAAO,KAAK,aAAa,MAAM,QAAQ,UAAU;AAAA,IACnD;AACA,WAAO,KAAK,QAAQ,QAAQ,cAAc,IAAI;AAAA,EAChD;AAAA,EAEA,MAAM,OAAO,OAAyC;AACpD,WAAO,KAAK,QAAQ,OAAO,iBAAiB,mBAAmB,KAAK,CAAC,EAAE;AAAA,EACzE;AAAA;AAAA,EAIQ,aACN,MACA,YACkB;AAClB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,MAAM,IAAI,eAAe;AAC/B,UAAI,KAAK,QAAQ,GAAG,KAAK,OAAO,YAAY;AAC5C,UAAI,eAAe;AACnB,UAAI,kBAAkB;AACtB,UAAI,OAAO,aAAa,CAAC,UAAU;AACjC,YAAI,CAAC,WAAY;AACjB,mBAAW;AAAA,UACT,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,SAAS,MAAM,QAAQ,IAAI,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG,IAAI;AAAA,QAC9E,CAAC;AAAA,MACH;AACA,UAAI,SAAS,MAAM;AACjB,cAAM,OAAO,IAAI;AAIjB,YAAI,IAAI,UAAU,OAAO,IAAI,SAAS,OAAO,MAAM,MAAM;AACvD,kBAAQ,KAAK,IAAI;AACjB;AAAA,QACF;AACA;AAAA,UACE,IAAI;AAAA,YACF,IAAI;AAAA,YACJ,MAAM,OAAO,QAAQ;AAAA,YACrB,MAAM,OAAO,WAAW,kBAAkB,IAAI,MAAM;AAAA,UACtD;AAAA,QACF;AAAA,MACF;AACA,UAAI,UAAU,MACZ,OAAO,IAAI,eAAe,GAAG,iBAAiB,6BAA6B,CAAC;AAC9E,UAAI,KAAK,IAAI;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,QAAW,QAAgB,MAAc,MAA4B;AACjF,UAAM,aAAa,SAAS,UAAa,EAAE,gBAAgB;AAC3D,UAAM,WAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAC9D;AAAA,MACA,SAAS,aAAa,EAAE,gBAAgB,mBAAmB,IAAI;AAAA,MAC/D,MAAM,SAAS,SAAY,SAAY,aAAa,KAAK,UAAU,IAAI,IAAK;AAAA,MAC5E,aAAa;AAAA,IACf,CAAC;AAED,QAAI,SAAS,WAAW,IAAK,QAAO;AAEpC,UAAM,OAAgB,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AAC5D,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,QAAS,MAA+D;AAC9E,YAAM,IAAI;AAAA,QACR,SAAS;AAAA,QACT,OAAO,QAAQ;AAAA,QACf,OAAO,WAAW,mBAAmB,SAAS,MAAM;AAAA,MACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;ACtPA,IAAM,oBAA4C;AAAA;AAAA,EAEhD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA;AAAA,EAGN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAGL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA;AAAA,EAGJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEO,IAAM,oBAAoB;AAG1B,SAAS,cAAc,UAA0B;AACtD,QAAM,OAAO,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK;AAC9C,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI,OAAO,KAAK,QAAQ,KAAK,SAAS,EAAG,QAAO;AAChD,SAAO,KAAK,MAAM,MAAM,CAAC,EAAE,YAAY;AACzC;AAGO,SAAS,iBAAiB,UAA0B;AACzD,SAAO,kBAAkB,cAAc,QAAQ,CAAC,KAAK;AACvD;AAKA,IAAM,sBAAqD;AAAA,EACzD,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,mBAAmB,KAAK;AAAA,EACzB,CAAC,mBAAmB,SAAS;AAAA,EAC7B,CAAC,qBAAqB,SAAS;AAAA,EAC/B,CAAC,oBAAoB,SAAS;AAAA,EAC9B,CAAC,+BAA+B,SAAS;AAAA,EACzC,CAAC,uBAAuB,SAAS;AAAA,EACjC,CAAC,uBAAuB,SAAS;AAAA,EACjC,CAAC,oBAAoB,SAAS;AAAA,EAC9B,CAAC,SAAS,MAAM;AAAA,EAChB,CAAC,oBAAoB,MAAM;AAAA,EAC3B,CAAC,mBAAmB,MAAM;AAAA,EAC1B,CAAC,oBAAoB,MAAM;AAAA,EAC3B,CAAC,0BAA0B,MAAM;AAAA,EACjC,CAAC,iBAAiB,MAAM;AAC1B;AAEA,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,aAAa,UAAgC;AAC3D,aAAW,CAAC,QAAQ,QAAQ,KAAK,qBAAqB;AACpD,QAAI,SAAS,WAAW,MAAM,EAAG,QAAO;AAAA,EAC1C;AACA,MAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,SAAO;AACT;;;AC7EO,IAAM,cAAc;AAAA,EACzB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,yBAAyB;AAAA,EACzB,gBAAgB;AAAA,EAChB,UAAU;AACZ;;;AClEO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAAiB,QAAgB,SAAiB,SAAmB;AAC/E,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AACvB,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;AAEO,IAAM,kBAAN,cAA8B,YAAY;AAAA,EAC/C,YAAY,SAAiB,SAAmB;AAC9C,UAAM,YAAY,YAAY,KAAK,SAAS,OAAO;AAAA,EACrD;AACF;AAEO,IAAM,oBAAN,cAAgC,YAAY;AAAA,EACjD,YAAY,UAAU,2BAA2B;AAC/C,UAAM,YAAY,iBAAiB,KAAK,OAAO;AAAA,EACjD;AACF;AAEO,IAAM,iBAAN,cAA6B,YAAY;AAAA,EAC9C,YAAY,UAAU,2CAA2C;AAC/D,UAAM,YAAY,WAAW,KAAK,OAAO;AAAA,EAC3C;AACF;AAGO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAC7C,YAAY,UAAU,sBAAsB;AAC1C,UAAM,YAAY,WAAW,KAAK,OAAO;AAAA,EAC3C;AACF;AAcO,IAAM,mBAAN,cAA+B,YAAY;AAAA,EAChD,YAAY,UAAU,yCAAyC;AAC7D,UAAM,YAAY,cAAc,KAAK,OAAO;AAAA,EAC9C;AACF;AAGO,IAAM,6BAAN,cAAyC,YAAY;AAAA,EAC1D,cAAc;AACZ,UAAM,YAAY,yBAAyB,KAAK,6CAA6C;AAAA,EAC/F;AACF;AAGO,IAAM,uBAAN,cAAmC,YAAY;AAAA,EACpD,YAAY,SAAiB,SAAmB;AAC9C,UAAM,YAAY,gBAAgB,KAAK,SAAS,OAAO;AAAA,EACzD;AACF;;;AC5EA,SAAS,kBAAkB;AAEpB,SAAS,UAAU,MAA0B;AAClD,SAAO,WAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvD;;;ACeO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAA6B,UAA2B;AAA3B;AAAA,EAA4B;AAAA,EAA5B;AAAA,EAE7B,IAAI,eAAuB;AACzB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA,EAGA,MAAM,OAAO,OAAwD;AACnE,UAAM,SAAS,UAAU,MAAM,IAAI;AACnC,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,KAAK,SAAS,IAAI,KAAK;AAAA,IACxC,SAAS,OAAO;AACd,YAAM,iBAAiB,iBAAiB,KAAK;AAAA,IAC/C;AACA,WAAO,EAAE,QAAQ,QAAQ,MAAM,MAAM,KAAK,WAAW;AAAA,EACvD;AAAA,EAEA,MAAM,SAAS,KAAiC;AAC9C,QAAI;AACF,aAAO,MAAM,KAAK,SAAS,IAAI,GAAG;AAAA,IACpC,SAAS,OAAO;AACd,YAAM,iBAAiB,mBAAmB,KAAK;AAAA,IACjD;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC1C,QAAI;AACF,YAAM,KAAK,SAAS,OAAO,GAAG;AAAA,IAChC,SAAS,OAAO;AACd,YAAM,iBAAiB,iBAAiB,KAAK;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,MAAc,gBAA0C;AAC5E,WAAO,UAAU,IAAI,MAAM;AAAA,EAC7B;AACF;AAEO,SAAS,iBAAiB,SAAiB,OAAsC;AACtF,MAAI,iBAAiB,qBAAsB,QAAO;AAClD,QAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,SAAO,IAAI,qBAAqB,SAAS,GAAG,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK;AACnF;;;AC3DO,SAAS,iBAAiB,MAAsB;AAErD,QAAM,OAAO,KAAK,MAAM,OAAO,EAAE,IAAI,KAAK;AAE1C,QAAM,UAAU,KAEb,QAAQ,0BAA0B,EAAE,EACpC,QAAQ,cAAc,GAAG,EACzB,KAAK;AACR,MAAI,CAAC,WAAW,YAAY,OAAO,YAAY,KAAM,QAAO;AAC5D,SAAO,QAAQ,MAAM,GAAG,GAAG;AAC7B;;;ACIO,IAAM,cAAN,MAAkB;AAAA,EACvB,YACmB,OACA,SACA,WACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAHgB;AAAA,EACA;AAAA,EACA;AAAA,EAGnB,MAAM,OACJ,QACA,OACqB;AACrB,UAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAI,CAAC,KAAM,OAAM,IAAI,gBAAgB,mBAAmB;AAExD,QAAI,MAAM,aAAa,MAAM;AAC3B,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM,QAAQ;AACzD,UAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AAAA,IACrF;AAEA,UAAM,WAAW,iBAAiB,IAAI;AACtC,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAG1C,UAAM,EAAE,QAAQ,QAAQ,KAAK,IAAI,MAAM,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,MAAM,KAAK,CAAC;AAEzF,WAAO,KAAK,MAAM,OAAO;AAAA,MACvB;AAAA,MACA,UAAU,MAAM;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,mBAAmB,OAAO,OAAO,SAAS;AAAA,IAC5C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,KAAK,QAAgB,UAAgD;AACzE,QAAI,aAAa,MAAM;AACrB,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,QAAQ;AACnD,UAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AAAA,IACrF;AACA,WAAO,KAAK,MAAM,aAAa,QAAQ,QAAQ;AAAA,EACjD;AAAA,EAEA,MAAM,IAAI,QAAgB,IAAiC;AACzD,UAAM,SAAS,MAAM,KAAK,MAAM,SAAS,EAAE;AAC3C,QAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,gBAAgB;AACjF,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,QAAgB,IAAY,MAAmC;AAC1E,UAAM,KAAK,IAAI,QAAQ,EAAE;AACzB,UAAM,QAAQ,iBAAiB,IAAI;AACnC,QAAI,CAAC,MAAO,OAAM,IAAI,gBAAgB,mBAAmB;AACzD,WAAO,KAAK,MAAM,OAAO,IAAI,EAAE,MAAM,MAAM,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,KAAK,QAAgB,IAAY,UAA8C;AACnF,UAAM,KAAK,IAAI,QAAQ,EAAE;AACzB,QAAI,aAAa,MAAM;AACrB,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,QAAQ;AACnD,UAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AAAA,IACrF;AACA,WAAO,KAAK,MAAM,OAAO,IAAI,EAAE,SAAS,CAAC;AAAA,EAC3C;AAAA,EAEA,MAAM,OAAO,QAAgB,IAA2B;AACtD,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,QAAI;AACF,YAAM,OAAO,OAAO,EAAE,WAAW,OAAO,OAAO,iBAAiB,EAAE,CAAC;AAAA,IACrE,QAAQ;AAAA,IAGR;AACA,UAAM,KAAK,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;AAAA,EACzC;AAAA,EAEA,MAAM,SAAS,QAAgB,IAAmC;AAChE,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,UAAM,OAAO,MAAM,OAAO,SAAS,EAAE,WAAW,OAAO,OAAO,iBAAiB,EAAE,CAAC;AAClF,UAAM,oBAAoB,MAAM,OAAO,gBAAgB,MAAM,OAAO,MAAM;AAC1E,WAAO,EAAE,QAAQ,MAAM,kBAAkB;AAAA,EAC3C;AACF;;;ACvFO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACmB,SACA,OACA,WACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAHgB;AAAA,EACA;AAAA,EACA;AAAA,EAGnB,MAAM,OACJ,QACA,OACuB;AACvB,UAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAI,CAAC,KAAM,OAAM,IAAI,gBAAgB,qBAAqB;AAC1D,QAAI,MAAM,aAAa,MAAM;AAC3B,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM,QAAQ;AACzD,UAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AAAA,IACrF;AACA,WAAO,KAAK,QAAQ,OAAO,EAAE,QAAQ,UAAU,MAAM,UAAU,KAAK,CAAC;AAAA,EACvE;AAAA,EAEA,MAAM,aAAa,QAAgB,UAAkD;AACnF,QAAI,aAAa,MAAM;AACrB,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,QAAQ;AACnD,UAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AAAA,IACrF;AACA,WAAO,KAAK,QAAQ,aAAa,QAAQ,QAAQ;AAAA,EACnD;AAAA,EAEA,MAAM,KAAK,QAAyC;AAClD,WAAO,KAAK,QAAQ,WAAW,MAAM;AAAA,EACvC;AAAA,EAEA,MAAM,IAAI,QAAgB,IAAmC;AAC3D,UAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,EAAE;AAC7C,QAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AACnF,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,QAAgB,IAAY,MAAqC;AAC5E,UAAM,KAAK,IAAI,QAAQ,EAAE;AACzB,UAAM,QAAQ,iBAAiB,IAAI;AACnC,QAAI,CAAC,MAAO,OAAM,IAAI,gBAAgB,qBAAqB;AAC3D,WAAO,KAAK,QAAQ,OAAO,IAAI,EAAE,MAAM,MAAM,CAAC;AAAA,EAChD;AAAA,EAEA,MAAM,KAAK,QAAgB,IAAY,aAAmD;AACxF,UAAM,KAAK,IAAI,QAAQ,EAAE;AAEzB,QAAI,gBAAgB,GAAI,OAAM,IAAI,gBAAgB,kCAAkC;AAEpF,QAAI,gBAAgB,MAAM;AAGxB,UAAI,SAAwB;AAC5B,aAAO,WAAW,MAAM;AACtB,YAAI,WAAW,IAAI;AACjB,gBAAM,IAAI,gBAAgB,iDAAiD;AAAA,QAC7E;AACA,cAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM;AACjD,YAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB;AACnF,iBAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAEA,WAAO,KAAK,QAAQ,OAAO,IAAI,EAAE,UAAU,YAAY,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,OAAO,QAAgB,IAA2C;AACtE,UAAM,KAAK,IAAI,QAAQ,EAAE;AAEzB,UAAM,MAAM,MAAM,KAAK,QAAQ,WAAW,MAAM;AAChD,UAAM,aAAa,oBAAI,IAA6B;AACpD,eAAW,UAAU,KAAK;AACxB,YAAM,OAAO,WAAW,IAAI,OAAO,QAAQ,KAAK,CAAC;AACjD,WAAK,KAAK,OAAO,EAAE;AACnB,iBAAW,IAAI,OAAO,UAAU,IAAI;AAAA,IACtC;AAGA,UAAM,SAAmB,CAAC,EAAE;AAC5B,UAAM,QAAQ,CAAC,EAAE;AACjB,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,UAAU,MAAM,MAAM;AAC5B,iBAAW,SAAS,WAAW,IAAI,OAAO,KAAK,CAAC,GAAG;AACjD,eAAO,KAAK,KAAK;AACjB,cAAM,KAAK,KAAK;AAAA,MAClB;AAAA,IACF;AACA,UAAM,YAAY,IAAI,IAAI,MAAM;AAEhC,UAAM,YAAY,MAAM,KAAK,MAAM,WAAW,MAAM;AACpD,UAAM,gBAAgB,UAAU,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ,UAAU,IAAI,EAAE,QAAQ,CAAC;AAK9F,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,eAAW,QAAQ,eAAe;AAChC,UAAI;AACF,cAAM,OAAO,OAAO,EAAE,WAAW,OAAO,KAAK,iBAAiB,EAAE,CAAC;AAAA,MACnE,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,KAAK,MAAM,WAAW,cAAc,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC1D,UAAM,KAAK,QAAQ,WAAW,MAAM;AAEpC,WAAO,EAAE,gBAAgB,OAAO,QAAQ,cAAc,cAAc,OAAO;AAAA,EAC7E;AACF;;;AC3HA,IAAM,mBAAmB;AAGlB,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACmB,OACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,MAAM,OACJ,QACA,OACA,QAAQ,IACmD;AAC3D,UAAM,IAAI,MAAM,KAAK;AACrB,QAAI,CAAC,EAAG,OAAM,IAAI,gBAAgB,gCAAgC;AAClE,QAAI,EAAE,SAAS,kBAAkB;AAC/B,YAAM,IAAI,gBAAgB,gCAAgC,gBAAgB,aAAa;AAAA,IACzF;AACA,UAAM,CAAC,aAAa,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,MACrD,KAAK,MAAM,aAAa,QAAQ,GAAG,KAAK;AAAA,MACxC,KAAK,QAAQ,aAAa,QAAQ,GAAG,KAAK;AAAA,IAC5C,CAAC;AACD,WAAO,EAAE,OAAO,aAAa,SAAS,cAAc;AAAA,EACtD;AACF;;;AC7BA,SAAS,KAAK,sBAAsB;AACpC,SAAS,qBAAqB;;;ACD9B,SAAS,gBAAgB;AAWlB,SAAS,mBAAmB,OAAmC;AACpE,SAAO,iBAAiB;AAC1B;AAGO,SAAS,wBAAwB,OAAyB;AAC/D,SAAO,mBAAmB,KAAK,KAAK,MAAM,iBAAiB;AAC7D;AAMO,SAAS,iBAAiB,OAAgB,iBAAsC;AACrF,MAAI,iBAAiB,YAAa,QAAO;AAEzC,MAAI,mBAAmB,KAAK,GAAG;AAC7B,UAAM,MAAM,MAAM,gBAAgB;AAElC,QAAI,IAAI,WAAW,aAAa,GAAG;AACjC,YAAM,UAAU,OAAO,IAAI,MAAM,cAAc,MAAM,CAAC,KAAK;AAC3D,aAAO,IAAI,iBAAiB,+CAA0C,OAAO,UAAU;AAAA,IACzF;AAEA,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,eAAO,IAAI,gBAAgB,gCAAgC;AAAA,MAC7D,KAAK;AACH,eAAO,IAAI,gBAAgB,kDAAkD;AAAA,MAC/E,KAAK;AACH,eAAO,IAAI,gBAAgB,2BAA2B;AAAA,MACxD,KAAK;AACH,eAAO,IAAI,gBAAgB,4DAAuD;AAAA,MACpF,KAAK;AACH,eAAO,IAAI,gBAAgB,oCAAoC;AAAA,MACjE,KAAK;AACH,eAAO,IAAI,kBAAkB,sDAAsD;AAAA,MACrF,KAAK;AACH,eAAO,IAAI,gBAAgB,+BAA+B;AAAA,MAC5D,KAAK;AACH,eAAO,IAAI,eAAe,2CAA2C;AAAA,MACvE,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,kBAAkB,iEAA4D;AAAA,MAC3F,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,2BAA2B;AAAA,MACxC;AACE,eAAO,IAAI,qBAAqB,iBAAiB;AAAA,UAC/C,MAAM,MAAM;AAAA,UACZ,eAAe;AAAA,QACjB,CAAC;AAAA,IACL;AAAA,EACF;AAEA,SAAO,IAAI;AAAA,IACT,iBAAiB,QAAQ,GAAG,eAAe,KAAK,MAAM,OAAO,KAAK;AAAA,IAClE;AAAA,EACF;AACF;;;ADvDO,IAAM,wBAAN,MAA4B;AAAA,EAGjC,YACmB,aACA,UACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAJX,UAAU,oBAAI,IAAqC;AAAA,EAO3D,MAAM,WAAW,QAAyC;AACxD,UAAM,SAAS,KAAK,QAAQ,IAAI,MAAM;AACtC,QAAI,QAAQ;AACV,UAAI;AACF,eAAO,MAAM;AAAA,MACf,QAAQ;AACN,aAAK,QAAQ,OAAO,MAAM;AAAA,MAC5B;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,KAAK,SAAS,IAAI,MAAM;AAC9C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,kBAAkB,mCAAmC;AAAA,IACjE;AAEA,UAAM,UAAU,KAAK,QAAQ,QAAQ,aAAa;AAClD,SAAK,QAAQ,IAAI,QAAQ,OAAO;AAChC,QAAI;AACF,aAAO,MAAM;AAAA,IACf,SAAS,OAAO;AACd,WAAK,QAAQ,OAAO,MAAM;AAC1B,YAAM,iBAAiB,OAAO,+BAA+B;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,QAAgB,QAA8B;AACxD,SAAK,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAAA,EAClD;AAAA,EAEA,MAAM,gBAA+B;AACnC,eAAW,CAAC,QAAQ,OAAO,KAAK,KAAK,SAAS;AAC5C,WAAK,QAAQ,OAAO,MAAM;AAC1B,UAAI;AACF,cAAM,SAAS,MAAM;AACrB,cAAM,OAAO,WAAW;AAAA,MAC1B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,QAAQ,eAAgD;AACpE,UAAM,SAAS,IAAI;AAAA,MACjB,IAAI,cAAc,aAAa;AAAA,MAC/B,KAAK,YAAY;AAAA,MACjB,KAAK,YAAY;AAAA,MACjB,EAAE,mBAAmB,EAAE;AAAA,IACzB;AACA,UAAM,OAAO,QAAQ;AACrB,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,aAAkD;AAClF,SAAO,IAAI,eAAe,IAAI,cAAc,EAAE,GAAG,YAAY,OAAO,YAAY,SAAS;AAAA,IACvF,mBAAmB;AAAA,EACrB,CAAC;AACH;AAMO,SAAS,sBAAsB,QAAsC;AAC1E,QAAM,QACJ,kBAAkB,IAAI,WAAW,kBAAkB,IAAI,kBAAkB,OAAO,QAAQ,CAAC;AAC3F,aAAW,QAAQ,OAAO;AACxB,QAAI,gBAAgB,IAAI,QAAS,QAAO;AAAA,EAC1C;AACA,QAAM,IAAI,qBAAqB,qDAAqD;AACtF;;;AE/FA,SAAS,OAAAA,YAA2B;AACpC,OAAO,YAAY;AACnB,SAAS,kBAAkB;AAuBpB,IAAM,0BAAN,MAAyD;AAAA,EAI9D,YACmB,QACjB,SACA;AAFiB;AAGjB,SAAK,OAAO,IAAIC,KAAI,iBAAiB;AAAA,MACnC,WAAW,OAAO,QAAQ,MAAM;AAAA,MAChC,YAAY,OAAO,QAAQ,UAAU;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAPmB;AAAA,EAJV,OAAO;AAAA,EACC;AAAA,EAYjB,MAAM,IAAI,OAAkD;AAC1D,QAAI;AACF,YAAM,OAAO,IAAI,WAAW,MAAM,MAAM,MAAM,KAAK,YAAY,IAAI,MAAM,IAAI;AAC7E,YAAM,UAAU,MAAM,KAAK,OAAO,SAAS,KAAK,MAAM;AAAA,QACpD;AAAA,QACA,eAAe;AAAA,QACf,SAAS;AAAA,MACX,CAAC;AACD,UAAI,EAAE,mBAAmBA,KAAI,UAAU;AACrC,cAAM,IAAI,qBAAqB,iDAAiD;AAAA,MAClF;AACA,aAAO;AAAA,QACL,WAAW,QAAQ,GAAG,SAAS;AAAA,QAC/B,MAAM,MAAM;AAAA,QACZ,MAAM,MAAM,KAAK;AAAA,QACjB,UAAU,MAAM;AAAA,MAClB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,IAAI,KAAiC;AACzC,QAAI;AACJ,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,WAAW,IAAI,SAAS;AACnD,UAAI,CAAC,SAAS,OAAO;AACnB,cAAM,IAAI,qBAAqB,0CAA0C;AAAA,MAC3E;AACA,aAAO,MAAM,KAAK,OAAO,cAAc,SAAS,CAAC,CAAC;AAAA,IACpD,SAAS,OAAO;AACd,UAAI,iBAAiB,qBAAsB,OAAM;AACjD,YAAM,iBAAiB,OAAO,0BAA0B;AAAA,IAC1D;AACA,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,YAAM,IAAI,qBAAqB,sCAAsC;AAAA,IACvE;AACA,WAAO,OAAO,KAAK,IAAI;AAAA,EACzB;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC1C,QAAI;AACF,YAAM,KAAK,OAAO,eAAe,KAAK,MAAM,CAAC,OAAO,IAAI,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACvF,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAAkC;AAC7C,QAAI;AACF,aAAQ,MAAM,KAAK,KAAK,GAAG,MAAO;AAAA,IACpC,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAA8C;AACvD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,WAAW,IAAI,SAAS;AAAA,IAC/C,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AACA,QAAI,CAAC,SAAS,MAAO,QAAO;AAE5B,UAAM,WAAW,QAAQ;AACzB,QAAI,EAAE,oBAAoBA,KAAI,UAAW,QAAO;AAEhD,QAAI;AACJ,eAAW,QAAQ,SAAS,YAAY;AACtC,UAAI,gBAAgBA,KAAI,2BAA2B;AACjD,mBAAW,KAAK;AAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,WAAW,IAAI;AAAA,MACf,MAAM,YAAY;AAAA,MAClB,MAAM,OAAO,SAAS,KAAK,SAAS,CAAC;AAAA,MACrC,UAAU,SAAS;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,IAA8C;AACrE,UAAM,WAAW,MAAM,KAAK,OAAO,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;AAC/E,UAAM,QAAQ,SAAS,CAAC;AACxB,WAAO,iBAAiBA,KAAI,UAAU,QAAQ;AAAA,EAChD;AACF;;;AC/HA,SAAS,OAAAC,YAA2B;AACpC,SAAS,oBAAoB;AAiBtB,IAAM,oBAAoB;AAajC,IAAM,oBAAoB,IAAI,KAAK;AAS5B,IAAM,4BAAN,MAAgC;AAAA,EAGrC,YACmB,OACA,eACA,aACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAHgB;AAAA,EACA;AAAA,EACA;AAAA,EALX,QAAQ,oBAAI,IAAuB;AAAA;AAAA,EAS3C,MAAM,WAAW,OAA8B;AAC7C,SAAK,kBAAkB;AACvB,UAAM,KAAK,YAAY,KAAK;AAE5B,UAAM,SAAS,kBAAkB,KAAK,WAAW;AACjD,QAAI;AACF,YAAM,OAAO,QAAQ;AACrB,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO;AAAA,QACrC,EAAE,OAAO,KAAK,YAAY,OAAO,SAAS,KAAK,YAAY,QAAQ;AAAA,QACnE;AAAA,MACF;AACA,WAAK,MAAM,IAAI,OAAO,EAAE,OAAO,QAAQ,eAAe,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IAC/E,SAAS,OAAO;AACd,YAAM,KAAK,kBAAkB,MAAM;AACnC,YAAM,iBAAiB,OAAO,+CAA+C;AAAA,IAC/E;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,WAAW,OAAe,MAAyC;AACvE,UAAM,OAAO,KAAK,YAAY,KAAK;AACnC,QAAI;AACF,YAAM,KAAK,OAAO;AAAA,QAChB,IAAIC,KAAI,KAAK,OAAO;AAAA,UAClB,aAAa,KAAK;AAAA,UAClB,eAAe,KAAK;AAAA,UACpB,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AACd,UAAI,wBAAwB,KAAK,GAAG;AAClC,eAAO,EAAE,QAAQ,oBAAoB;AAAA,MACvC;AACA,YAAM,iBAAiB,OAAO,yBAAyB;AAAA,IACzD;AACA,WAAO,KAAK,cAAc,IAAI;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,eAAe,OAAe,UAA6C;AAC/E,UAAM,OAAO,KAAK,YAAY,KAAK;AACnC,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,OAAO,OAAO,IAAIA,KAAI,QAAQ,YAAY,CAAC;AAC3E,YAAM,QAAQ,MAAM,aAAa,cAAc,QAAQ;AACvD,YAAM,KAAK,OAAO,OAAO,IAAIA,KAAI,KAAK,cAAc,EAAE,UAAU,MAAM,CAAC,CAAC;AAAA,IAC1E,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,yBAAyB;AAAA,IACzD;AACA,WAAO,KAAK,cAAc,IAAI;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,YAAY,OAA8B;AAC9C,UAAM,OAAO,KAAK,MAAM,IAAI,KAAK;AACjC,QAAI,MAAM;AACR,WAAK,MAAM,OAAO,KAAK;AACvB,YAAM,KAAK,kBAAkB,KAAK,MAAM;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,QAAwC;AAC1D,UAAM,WAAW,MAAM,KAAK,MAAM,SAAS,sBAAsB,QAAQ,iBAAiB;AAC1F,QAAI,SAAU,QAAO;AAErB,UAAM,SAAS,MAAM,KAAK,cAAc,WAAW,MAAM;AACzD,WAAO,KAAK,uBAAuB,QAAQ,MAAM;AAAA,EACnD;AAAA,EAEA,MAAM,uBAAuB,QAAgB,QAAgD;AAC3F,UAAM,WAAW,MAAM,KAAK,MAAM,SAAS,sBAAsB,QAAQ,iBAAiB;AAC1F,QAAI,SAAU,QAAO;AAErB,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,OAAO;AAAA,QAC1B,IAAIA,KAAI,SAAS,cAAc;AAAA,UAC7B,OAAO;AAAA,UACP,OACE;AAAA,UACF,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,gBAAU,sBAAsB,MAAM;AAAA,IACxC,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,8CAA8C;AAAA,IAC9E;AAEA,UAAM,aAAa,QAAQ,YAAY,SAAS;AAChD,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,qBAAqB,kDAAkD;AAAA,IACnF;AAEA,WAAO,KAAK,MAAM,SAAS,OAAO;AAAA,MAChC;AAAA,MACA,UAAU;AAAA,MACV,OAAO,QAAQ,SAAS;AAAA,MACxB,gBAAgB,QAAQ,GAAG,SAAS;AAAA,MACpC,oBAAoB;AAAA,IACtB,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,YAAY,OAA0B;AAC5C,UAAM,OAAO,KAAK,MAAM,IAAI,KAAK;AACjC,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,cAAc,iDAAiD;AAAA,IAC3E;AACA,QAAI,KAAK,IAAI,IAAI,KAAK,YAAY,mBAAmB;AACnD,WAAK,MAAM,OAAO,KAAK;AACvB,WAAK,KAAK,kBAAkB,KAAK,MAAM;AACvC,YAAM,IAAI,gBAAgB,qDAAgD;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,cAAc,MAA4C;AACtE,SAAK,MAAM,OAAO,KAAK,KAAK;AAE5B,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,sBAAgB,KAAK,OAAO,QAAQ,KAAK;AACzC,YAAM,KAAK,MAAM,KAAK,OAAO,MAAM;AACnC,UAAI,EAAE,cAAcA,KAAI,OAAO;AAC7B,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AACA,aAAO,MAAM,KAAK,MAAM,MAAM,mBAAmB;AAAA,QAC/C,gBAAgB,GAAG,GAAG,SAAS;AAAA,QAC/B,UAAU,GAAG,YAAY;AAAA,QACzB,WAAW,GAAG,aAAa;AAAA,QAC3B,UAAU,GAAG,YAAY;AAAA,QACzB,OAAO,GAAG,SAAS,KAAK;AAAA,MAC1B,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,KAAK,kBAAkB,KAAK,MAAM;AACxC,YAAM,iBAAiB,OAAO,4CAA4C;AAAA,IAC5E;AAEA,UAAM,KAAK,MAAM,SAAS,KAAK,KAAK,IAAI,aAAa;AACrD,UAAM,KAAK,uBAAuB,KAAK,IAAI,KAAK,MAAM;AACtD,SAAK,cAAc,YAAY,KAAK,IAAI,KAAK,MAAM;AAEnD,WAAO,EAAE,QAAQ,MAAM,KAAK;AAAA,EAC9B;AAAA,EAEQ,oBAA0B;AAChC,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,OAAO,IAAI,KAAK,KAAK,OAAO;AACtC,UAAI,MAAM,KAAK,YAAY,mBAAmB;AAC5C,aAAK,MAAM,OAAO,KAAK;AACvB,aAAK,KAAK,kBAAkB,KAAK,MAAM;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,QAAuC;AACrE,QAAI;AACF,YAAM,OAAO,WAAW;AAAA,IAC1B,QAAQ;AAAA,IAER;AAAA,EACF;AACF;","names":["Api","Api","Api","Api"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../../shared/src/mime.ts","../../shared/src/query.ts","../../shared/src/index.ts","../../core/src/errors.ts","../../core/src/storage/engine.ts","../../core/src/utils/hash.ts","../../core/src/services/query-resolver.ts","../../core/src/services/activity-service.ts","../../core/src/utils/filename.ts","../../core/src/services/file-service.ts","../../core/src/services/folder-service.ts","../../core/src/services/search-service.ts","../../core/src/services/stats-service.ts","../../core/src/services/trash-service.ts","../../core/src/services/integrity-service.ts","../../core/src/services/recent-service.ts","../../telegram/src/client.ts","../../telegram/src/errors.ts","../../telegram/src/provider.ts","../../telegram/src/connection.ts"],"sourcesContent":["import type {\n ActivityPageDTO,\n BatchResultDTO,\n FolderMutationResultDTO,\n EmptyTrashResultDTO,\n FileDTO,\n FileVersionDTO,\n FoldersPageDTO,\n FilesPageDTO,\n FolderDTO,\n HealthDTO,\n IntegrityReportDTO,\n ListQuery,\n RecentPageDTO,\n SearchResultDTO,\n SessionInfo,\n StorageDTO,\n StorageHealthDTO,\n StorageStatsDTO,\n TelegramVerifyResponse,\n TrashPageDTO,\n UserDTO,\n} from \"@omnicloud/shared\";\n\n/**\n * Errors surfaced by the SDK. `code` mirrors the server's stable error codes\n * and `requestId` correlates with server logs.\n */\nexport class OmniCloudError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n public readonly requestId: string | undefined = undefined,\n public readonly details: unknown = undefined,\n ) {\n super(message);\n this.name = \"OmniCloudError\";\n }\n}\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Base backoff in ms (doubles per retry). Default 300. */\n baseDelayMs?: number;\n}\n\nexport interface OmniCloudClientOptions {\n /** Base URL of an OmniCloud server (e.g. \"https://cloud.example\"). */\n baseUrl?: string;\n /** Bearer token, when the deployment uses token auth instead of cookies. */\n token?: string;\n /** Custom fetch implementation (tests, polyfills). */\n fetchImpl?: typeof fetch;\n /** Retry policy for transient failures. */\n retry?: RetryOptions;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n}\n\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percent: number;\n}\n\nexport interface UploadOptions {\n folderId?: string | null;\n onProgress?: (progress: UploadProgress) => void;\n /** Aborts the upload. */\n signal?: AbortSignal;\n /** Number of retry attempts for failed uploads. Default 2. */\n retry?: number;\n}\n\nexport interface UploadInput {\n /** File content: Blob/File in the browser, Buffer/Uint8Array in Node. */\n data: Blob | Uint8Array;\n /** Filename; also used to derive the MIME type server-side. */\n name: string;\n}\n\nexport interface DownloadProgress {\n loaded: number;\n total: number | null;\n percent: number | null;\n}\n\nexport interface DownloadResult {\n data: Buffer | Uint8Array;\n contentType: string;\n size: number;\n sha256: string | null;\n integrityVerified: boolean | null;\n}\n\ntype Query = Record<string, string | number | boolean | undefined | null>;\n\nconst DEFAULT_RETRY: Required<RetryOptions> = { attempts: 3, baseDelayMs: 300 };\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer);\n reject(new OmniCloudError(0, \"ABORTED\", \"Request aborted\"));\n },\n { once: true },\n );\n });\n}\n\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 429 || status >= 500;\n}\n\n/** Builds a query string, skipping undefined/null/empty values. */\nfunction buildQuery(query: Query | ListQuery | undefined): string {\n const params = new URLSearchParams();\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null || value === \"\") continue;\n params.set(key, String(value));\n }\n }\n const encoded = params.toString();\n return encoded ? `?${encoded}` : \"\";\n}\n\nfunction safeJsonParse(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\n/**\n * Naming: every endpoint group is a namespaced sub-client so calls read like\n *\n * cloud.files.list({ folderId })\n * cloud.trash.empty()\n * cloud.storage.stats()\n *\n * Works in browsers (cookie sessions, XHR upload progress) and Node\n * (fetch, Buffer downloads).\n */\nexport class OmniCloudClient {\n readonly files: FilesApi;\n readonly folders: FoldersApi;\n readonly trash: TrashApi;\n readonly storage: StorageApi;\n readonly search: SearchApi;\n readonly recent: RecentApi;\n readonly activity: ActivityApi;\n readonly auth: AuthApi;\n\n /** @internal */\n readonly baseUrl: string;\n /** @internal */\n readonly fetchImpl: typeof fetch;\n /** @internal */\n readonly retry: Required<RetryOptions>;\n /** @internal */\n readonly defaultHeaders: Record<string, string>;\n\n constructor(options: OmniCloudClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? \"\").replace(/\\/$/, \"\");\n this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);\n this.retry = { ...DEFAULT_RETRY, ...options.retry };\n this.defaultHeaders = {\n ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}),\n ...options.headers,\n };\n\n this.files = new FilesApi(this);\n this.folders = new FoldersApi(this);\n this.trash = new TrashApi(this);\n this.storage = new StorageApi(this);\n this.search = new SearchApi(this);\n this.recent = new RecentApi(this);\n this.activity = new ActivityApi(this);\n this.auth = new AuthApi(this);\n }\n\n /** Low-level request helper (retries transient failures). */\n async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n return this.withRetry(async () => this.rawRequest<T>(method, path, body));\n }\n\n /** @internal */\n async rawRequest<T>(method: string, path: string, body?: unknown): Promise<T> {\n const isForm = typeof FormData !== \"undefined\" && body instanceof FormData;\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: {\n ...this.defaultHeaders,\n ...(body !== undefined && !isForm ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: body === undefined ? undefined : isForm ? (body as FormData) : JSON.stringify(body),\n credentials: \"include\",\n });\n\n if (response.status === 204) return undefined as T;\n\n const text = await response.text();\n const data: unknown = text ? safeJsonParse(text) : null;\n\n if (!response.ok) {\n const error = (\n data as {\n error?: { code: string; message: string; requestId?: string; details?: unknown };\n } | null\n )?.error;\n throw new OmniCloudError(\n response.status,\n error?.code ?? \"INTERNAL_ERROR\",\n error?.message ?? `Request failed (${response.status})`,\n error?.requestId,\n error?.details,\n );\n }\n return data as T;\n }\n\n /** @internal */\n async withRetry<T>(operation: () => Promise<T>): Promise<T> {\n const { attempts, baseDelayMs } = this.retry;\n let lastError: unknown;\n for (let attempt = 1; attempt <= Math.max(1, attempts); attempt += 1) {\n try {\n return await operation();\n } catch (error) {\n lastError = error;\n const retryable = error instanceof OmniCloudError && isRetryableStatus(error.status);\n if (!retryable || attempt >= attempts) break;\n await sleep(baseDelayMs * 2 ** (attempt - 1));\n }\n }\n throw lastError;\n }\n\n /** @internal */\n async requestBinary(\n path: string,\n onProgress?: (p: DownloadProgress) => void,\n ): Promise<DownloadResult> {\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n headers: this.defaultHeaders,\n credentials: \"include\",\n });\n\n if (!response.ok) {\n const text = await response.text();\n const error = (safeJsonParse(text) as { error?: { code: string; message: string } } | null)\n ?.error;\n throw new OmniCloudError(\n response.status,\n error?.code ?? \"DOWNLOAD_FAILED\",\n error?.message ?? `Download failed (${response.status})`,\n );\n }\n\n const total = Number(response.headers.get(\"content-length\") ?? 0) || null;\n const contentType = response.headers.get(\"content-type\") ?? \"application/octet-stream\";\n const sha256 = response.headers.get(\"x-content-sha256\");\n const integrityHeader = response.headers.get(\"x-integrity-verified\");\n const integrityVerified = integrityHeader === null ? null : integrityHeader === \"true\";\n\n const data = await readBody(response, total, onProgress);\n return { data, contentType, size: data.byteLength, sha256, integrityVerified };\n }\n}\n\n/** Reads a response body, reporting progress when the platform exposes it. */\nasync function readBody(\n response: Response,\n total: number | null,\n onProgress?: (p: DownloadProgress) => void,\n): Promise<Buffer | Uint8Array> {\n const body = response.body;\n if (!body) {\n const buffer = new Uint8Array(await response.arrayBuffer());\n onProgress?.({ loaded: buffer.byteLength, total: buffer.byteLength, percent: 100 });\n return buffer;\n }\n\n const chunks: Uint8Array[] = [];\n let loaded = 0;\n const reader = body.getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n loaded += value.byteLength;\n onProgress?.({\n loaded,\n total,\n percent: total ? Math.round((loaded / total) * 100) : null,\n });\n }\n }\n\n const merged = new Uint8Array(loaded);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n // Prefer Buffer in Node so callers can use Buffer APIs.\n const BufferCtor = (globalThis as { Buffer?: typeof Buffer }).Buffer;\n return BufferCtor ? BufferCtor.from(merged) : merged;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Namespaced APIs\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass FilesApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n /** `folderId: null` = root; omit it to list every active file. */\n async list(query: ListQuery = {}): Promise<FilesPageDTO> {\n return this.client.request<FilesPageDTO>(\"GET\", `/api/files${buildQuery(query)}`);\n }\n\n async get(id: string): Promise<FileDTO> {\n const { file } = await this.client.request<{ file: FileDTO }>(\"GET\", `/api/files/${id}`);\n return file;\n }\n\n async versions(id: string): Promise<FileVersionDTO[]> {\n const { versions } = await this.client.request<{ versions: FileVersionDTO[] }>(\n \"GET\",\n `/api/files/${id}/versions`,\n );\n return versions;\n }\n\n /**\n * Uploads a file with progress and retry support. Uses XMLHttpRequest when\n * available (browsers) so progress and cancellation work; falls back to\n * fetch elsewhere.\n */\n async upload(input: UploadInput, options: UploadOptions = {}): Promise<FileDTO> {\n const attempts = Math.max(1, (options.retry ?? 2) + 1);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= attempts; attempt += 1) {\n try {\n return await this.uploadOnce(input, options);\n } catch (error) {\n lastError = error;\n if (options.signal?.aborted) throw error;\n if (error instanceof OmniCloudError && !isRetryableStatus(error.status)) throw error;\n if (attempt < attempts) await sleep(300 * 2 ** (attempt - 1), options.signal);\n }\n }\n throw lastError;\n }\n\n private uploadOnce(input: UploadInput, options: UploadOptions): Promise<FileDTO> {\n const form = new FormData();\n const blob =\n typeof Blob !== \"undefined\" && input.data instanceof Blob\n ? input.data\n : new Blob([new Uint8Array(input.data as Uint8Array)]);\n form.append(\"file\", blob, input.name);\n if (options.folderId) form.append(\"folderId\", options.folderId);\n\n if (typeof XMLHttpRequest === \"function\") {\n return this.uploadViaXhr(form, options);\n }\n return this.client\n .request<{ file: FileDTO }>(\"POST\", \"/api/files\", form)\n .then((response) => response.file);\n }\n\n private uploadViaXhr(form: FormData, options: UploadOptions): Promise<FileDTO> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open(\"POST\", `${this.client.baseUrl}/api/files`);\n xhr.responseType = \"json\";\n xhr.withCredentials = true;\n\n const onAbort = () => xhr.abort();\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n xhr.upload.onprogress = (event) => {\n options.onProgress?.({\n loaded: event.loaded,\n total: event.total,\n percent: event.total > 0 ? Math.round((event.loaded / event.total) * 100) : 0,\n });\n };\n xhr.onload = () => {\n options.signal?.removeEventListener(\"abort\", onAbort);\n const body = xhr.response as {\n file?: FileDTO;\n error?: { code: string; message: string; requestId?: string };\n } | null;\n if (xhr.status >= 200 && xhr.status < 300 && body?.file) {\n options.onProgress?.({ loaded: 1, total: 1, percent: 100 });\n resolve(body.file);\n return;\n }\n reject(\n new OmniCloudError(\n xhr.status,\n body?.error?.code ?? \"UPLOAD_FAILED\",\n body?.error?.message ?? `Upload failed (${xhr.status})`,\n body?.error?.requestId,\n ),\n );\n };\n xhr.onerror = () =>\n reject(new OmniCloudError(0, \"NETWORK_ERROR\", \"Network error during upload\"));\n xhr.onabort = () => reject(new OmniCloudError(0, \"ABORTED\", \"Upload aborted\"));\n xhr.send(form);\n });\n }\n\n /** Uploads a new version of an existing file. */\n async replace(id: string, input: UploadInput): Promise<FileDTO> {\n const form = new FormData();\n const blob =\n typeof Blob !== \"undefined\" && input.data instanceof Blob\n ? input.data\n : new Blob([new Uint8Array(input.data as Uint8Array)]);\n form.append(\"file\", blob, input.name);\n const { file } = await this.client.request<{ file: FileDTO }>(\n \"POST\",\n `/api/files/${id}/replace`,\n form,\n );\n return file;\n }\n\n async download(id: string, onProgress?: (p: DownloadProgress) => void): Promise<DownloadResult> {\n return this.client.requestBinary(`/api/files/${id}/download`, onProgress);\n }\n\n /** URL suitable for direct download (anchor href). */\n downloadUrl(id: string): string {\n return `${this.client.baseUrl}/api/files/${id}/download`;\n }\n\n async rename(id: string, name: string): Promise<FileDTO> {\n const { file } = await this.client.request<{ file: FileDTO }>(\"PATCH\", `/api/files/${id}`, {\n name,\n });\n return file;\n }\n\n async star(id: string, starred = true): Promise<FileDTO> {\n const { file } = await this.client.request<{ file: FileDTO }>(\"PATCH\", `/api/files/${id}`, {\n starred,\n });\n return file;\n }\n\n async move(id: string, folderId: string | null): Promise<FileDTO> {\n const { file } = await this.client.request<{ file: FileDTO }>(\"POST\", `/api/files/${id}/move`, {\n folderId,\n });\n return file;\n }\n\n async trash(id: string): Promise<FileDTO> {\n const { file } = await this.client.request<{ file: FileDTO }>(\"POST\", `/api/files/${id}/trash`);\n return file;\n }\n\n async restore(id: string): Promise<FileDTO> {\n const { file } = await this.client.request<{ file: FileDTO }>(\n \"POST\",\n `/api/files/${id}/restore`,\n );\n return file;\n }\n\n /** Permanently deletes a file and its Telegram objects. */\n async delete(id: string): Promise<void> {\n await this.client.request<void>(\"DELETE\", `/api/files/${id}`);\n }\n\n /** Batch operation: trash | restore | delete | star | unstar | move. */\n async batch(\n operation: \"trash\" | \"restore\" | \"delete\" | \"star\" | \"unstar\" | \"move\",\n ids: string[],\n options: { folderId?: string | null } = {},\n ): Promise<BatchResultDTO> {\n return this.client.request<BatchResultDTO>(\"POST\", \"/api/files/batch\", {\n operation,\n ids,\n ...(operation === \"move\" ? { folderId: options.folderId ?? null } : {}),\n });\n }\n}\n\nclass FoldersApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n async list(query: ListQuery & { parentId?: string | null } = {}): Promise<FoldersPageDTO> {\n const { parentId, ...rest } = query;\n const path =\n parentId !== undefined\n ? `/api/folders?parentId=${encodeURIComponent(parentId ?? \"\")}`\n : `/api/folders${buildQuery(rest)}`;\n return this.client.request<FoldersPageDTO>(\"GET\", path);\n }\n\n /** The full active folder tree (for move dialogs). */\n async tree(): Promise<FolderDTO[]> {\n const { folders } = await this.client.request<{ folders: FolderDTO[] }>(\n \"GET\",\n \"/api/folders/tree\",\n );\n return folders;\n }\n\n async create(name: string, parentId: string | null = null): Promise<FolderDTO> {\n const { folder } = await this.client.request<{ folder: FolderDTO }>(\"POST\", \"/api/folders\", {\n name,\n parentId,\n });\n return folder;\n }\n\n async rename(id: string, name: string): Promise<FolderDTO> {\n const { folder } = await this.client.request<{ folder: FolderDTO }>(\n \"PATCH\",\n `/api/folders/${id}`,\n { name },\n );\n return folder;\n }\n\n async star(id: string, starred = true): Promise<FolderDTO> {\n const { folder } = await this.client.request<{ folder: FolderDTO }>(\n \"PATCH\",\n `/api/folders/${id}`,\n { starred },\n );\n return folder;\n }\n\n async move(id: string, parentId: string | null): Promise<FolderDTO> {\n const { folder } = await this.client.request<{ folder: FolderDTO }>(\n \"POST\",\n `/api/folders/${id}/move`,\n { parentId },\n );\n return folder;\n }\n\n async trash(id: string): Promise<FolderMutationResultDTO> {\n return this.client.request<FolderMutationResultDTO>(\"POST\", `/api/folders/${id}/trash`);\n }\n\n async restore(id: string): Promise<FolderMutationResultDTO> {\n return this.client.request<FolderMutationResultDTO>(\"POST\", `/api/folders/${id}/restore`);\n }\n\n /** Permanently deletes the folder subtree and its Telegram objects. */\n async delete(id: string): Promise<FolderMutationResultDTO> {\n return this.client.request<FolderMutationResultDTO>(\"DELETE\", `/api/folders/${id}`);\n }\n\n async batch(\n operation: \"trash\" | \"restore\" | \"delete\" | \"star\" | \"unstar\" | \"move\",\n ids: string[],\n options: { parentId?: string | null } = {},\n ): Promise<BatchResultDTO> {\n return this.client.request<BatchResultDTO>(\"POST\", \"/api/folders/batch\", {\n operation,\n ids,\n ...(operation === \"move\" ? { parentId: options.parentId ?? null } : {}),\n });\n }\n}\n\nclass TrashApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n async list(query: ListQuery = {}): Promise<TrashPageDTO> {\n return this.client.request<TrashPageDTO>(\"GET\", `/api/trash${buildQuery(query)}`);\n }\n\n /** Permanently removes everything in the Trash. */\n async empty(): Promise<EmptyTrashResultDTO> {\n return this.client.request<EmptyTrashResultDTO>(\"POST\", \"/api/trash/empty\");\n }\n}\n\nclass StorageApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n /** Creates the private storage channel if it does not exist yet. */\n async ensure(): Promise<StorageDTO> {\n const { storage } = await this.client.request<{ storage: StorageDTO }>(\n \"POST\",\n \"/api/storage/ensure\",\n );\n return storage;\n }\n\n async stats(): Promise<StorageStatsDTO> {\n const { stats } = await this.client.request<{ stats: StorageStatsDTO }>(\n \"GET\",\n \"/api/storage/stats\",\n );\n return stats;\n }\n\n async health(): Promise<{ health: StorageHealthDTO; stats: StorageStatsDTO }> {\n return this.client.request(\"GET\", \"/api/storage/health\");\n }\n\n /** Probes the storage backend; `deep` performs a real Telegram round-trip. */\n async check(deep = false): Promise<StorageHealthDTO> {\n const { health } = await this.client.request<{ health: StorageHealthDTO }>(\n \"POST\",\n \"/api/storage/health/check\",\n { deep },\n );\n return health;\n }\n\n /** Read-only integrity scan; nothing is repaired or deleted. */\n async integrityCheck(options: { deep?: boolean } = {}): Promise<IntegrityReportDTO> {\n const { report } = await this.client.request<{ report: IntegrityReportDTO }>(\n \"POST\",\n \"/api/storage/integrity/check\",\n { deep: options.deep ?? false },\n );\n return report;\n }\n\n /** Public server health (no authentication required). */\n async serverHealth(): Promise<HealthDTO> {\n return this.client.request<HealthDTO>(\"GET\", \"/api/health\");\n }\n}\n\nclass SearchApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n /**\n * Searches with the v0.2 query language:\n * `type:pdf`, `size:>100MB`, `folder:Projects`, `starred:true`, `after:2026-01-01`\n */\n async query(q: string, page: { page?: number; limit?: number } = {}): Promise<SearchResultDTO> {\n return this.client.request<SearchResultDTO>(\n \"GET\",\n `/api/search${buildQuery({ q, ...page } as Query)}`,\n );\n }\n}\n\nclass RecentApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n async list(page: { page?: number; limit?: number } = {}): Promise<RecentPageDTO> {\n return this.client.request<RecentPageDTO>(\"GET\", `/api/recent${buildQuery(page as Query)}`);\n }\n}\n\nclass ActivityApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n async list(page: { page?: number; limit?: number } = {}): Promise<ActivityPageDTO> {\n return this.client.request<ActivityPageDTO>(\"GET\", `/api/activity${buildQuery(page as Query)}`);\n }\n}\n\nclass AuthApi {\n constructor(private readonly client: OmniCloudClient) {}\n\n async me(): Promise<SessionInfo> {\n return this.client.request<SessionInfo>(\"GET\", \"/api/auth/me\");\n }\n\n async startTelegramLogin(phone: string): Promise<void> {\n await this.client.request(\"POST\", \"/api/auth/telegram/start\", { phone });\n }\n\n async verifyTelegramCode(phone: string, code: string): Promise<TelegramVerifyResponse> {\n return this.client.request<TelegramVerifyResponse>(\"POST\", \"/api/auth/telegram/verify\", {\n phone,\n code,\n });\n }\n\n async submitTelegramPassword(\n phone: string,\n password: string,\n ): Promise<{ status: \"ok\"; user: UserDTO }> {\n return this.client.request(\"POST\", \"/api/auth/telegram/password\", { phone, password });\n }\n\n async logout(): Promise<void> {\n await this.client.request(\"POST\", \"/api/auth/logout\");\n }\n}\n","/**\n * Filename-extension based MIME type detection.\n *\n * MIME types supplied by clients are never trusted: the API derives the MIME\n * type from the filename extension server-side. Shared so the web UI can use\n * the same logic for icons.\n */\n\nconst MIME_BY_EXTENSION: Record<string, string> = {\n // Documents\n pdf: \"application/pdf\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n odt: \"application/vnd.oasis.opendocument.text\",\n ods: \"application/vnd.oasis.opendocument.spreadsheet\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n csv: \"text/csv\",\n rtf: \"application/rtf\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n xml: \"application/xml\",\n yaml: \"application/yaml\",\n yml: \"application/yaml\",\n json: \"application/json\",\n epub: \"application/epub+zip\",\n\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n avif: \"image/avif\",\n heic: \"image/heic\",\n\n // Audio\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n flac: \"audio/flac\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n opus: \"audio/opus\",\n\n // Video\n mp4: \"video/mp4\",\n mkv: \"video/x-matroska\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mov: \"video/quicktime\",\n wmv: \"video/x-ms-wmv\",\n flv: \"video/x-flv\",\n\n // Archives\n zip: \"application/zip\",\n rar: \"application/vnd.rar\",\n \"7z\": \"application/x-7z-compressed\",\n tar: \"application/x-tar\",\n gz: \"application/gzip\",\n bz2: \"application/x-bzip2\",\n xz: \"application/x-xz\",\n\n // Other\n apk: \"application/vnd.android.package-archive\",\n exe: \"application/x-msdownload\",\n dmg: \"application/x-apple-diskimage\",\n iso: \"application/x-iso9660-image\",\n bin: \"application/octet-stream\",\n};\n\nexport const DEFAULT_MIME_TYPE = \"application/octet-stream\";\n\n/** Returns the file extension without the leading dot, lowercased. */\nexport function fileExtension(filename: string): string {\n const base = filename.split(/[/\\\\]/).pop() ?? \"\";\n const dot = base.lastIndexOf(\".\");\n if (dot <= 0 || dot === base.length - 1) return \"\";\n return base.slice(dot + 1).toLowerCase();\n}\n\n/** Derives a MIME type from the filename extension. Never trusts client input. */\nexport function mimeFromFilename(filename: string): string {\n return MIME_BY_EXTENSION[fileExtension(filename)] ?? DEFAULT_MIME_TYPE;\n}\n\nexport type FileCategory =\n \"image\" | \"video\" | \"audio\" | \"pdf\" | \"archive\" | \"document\" | \"text\" | \"other\";\n\nconst PREFIXED_CATEGORIES: Array<[string, FileCategory]> = [\n [\"image/\", \"image\"],\n [\"video/\", \"video\"],\n [\"audio/\", \"audio\"],\n [\"application/pdf\", \"pdf\"],\n [\"application/zip\", \"archive\"],\n [\"application/x-tar\", \"archive\"],\n [\"application/gzip\", \"archive\"],\n [\"application/x-7z-compressed\", \"archive\"],\n [\"application/vnd.rar\", \"archive\"],\n [\"application/x-bzip2\", \"archive\"],\n [\"application/x-xz\", \"archive\"],\n [\"text/\", \"text\"],\n [\"application/json\", \"text\"],\n [\"application/xml\", \"text\"],\n [\"application/yaml\", \"text\"],\n [\"application/javascript\", \"text\"],\n [\"text/markdown\", \"text\"],\n];\n\nconst OFFICE_MIMES = new Set([\n \"application/msword\",\n \"application/vnd.ms-excel\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"application/vnd.oasis.opendocument.text\",\n \"application/vnd.oasis.opendocument.spreadsheet\",\n \"application/rtf\",\n \"application/epub+zip\",\n]);\n\n/** Coarse category used for icon selection in the UI. */\nexport function fileCategory(mimeType: string): FileCategory {\n for (const [prefix, category] of PREFIXED_CATEGORIES) {\n if (mimeType.startsWith(prefix)) return category;\n }\n if (OFFICE_MIMES.has(mimeType)) return \"document\";\n return \"other\";\n}\n\nexport const FILE_CATEGORIES: readonly FileCategory[] = [\n \"image\",\n \"video\",\n \"audio\",\n \"pdf\",\n \"archive\",\n \"document\",\n \"text\",\n \"other\",\n] as const;\n\n/** MIME prefixes belonging to each category, for repository-level filtering. */\nconst CATEGORY_PREFIXES: Record<FileCategory, readonly string[]> = {\n image: [\"image/\"],\n video: [\"video/\"],\n audio: [\"audio/\"],\n pdf: [\"application/pdf\"],\n archive: [\n \"application/zip\",\n \"application/x-tar\",\n \"application/gzip\",\n \"application/x-7z-compressed\",\n \"application/vnd.rar\",\n \"application/x-bzip2\",\n \"application/x-xz\",\n ],\n text: [\n \"text/\",\n \"application/json\",\n \"application/xml\",\n \"application/yaml\",\n \"application/javascript\",\n ],\n document: [...OFFICE_MIMES],\n other: [],\n};\n\nexport function isFileCategory(value: string): value is FileCategory {\n return (FILE_CATEGORIES as readonly string[]).includes(value);\n}\n\n/**\n * Expands a `type:` filter into a set of MIME matchers.\n *\n * Accepts either a coarse category (\"image\", \"pdf\") or an exact MIME type\n * (\"application/pdf\"); the repository layer turns these into prefix/equality\n * filters. Returns null when the value matches nothing usable.\n */\nexport function mimeMatchersForType(value: string): readonly string[] | null {\n const normalized = value.toLowerCase().trim();\n if (!normalized) return null;\n if (isFileCategory(normalized)) {\n const prefixes = CATEGORY_PREFIXES[normalized];\n return prefixes.length > 0 ? prefixes : null;\n }\n // Exact MIME type — match it as an exact value.\n return [normalized];\n}\n","/**\n * Search query parsing, pagination and sorting primitives.\n *\n * Self-contained (no runtime imports from the package root) so the API, web\n * app and SDK can all share one query language without import cycles.\n *\n * Query language (v0.2):\n * report → name contains \"report\"\n * name:report → same, explicit\n * type:pdf type:image type:application/zip\n * ext:zip → file extension (no dot)\n * size:>100MB size:<1KB size:5mb\n * folder:Projects → folder-name filter\n * starred:true trashed:true\n * after:2026-01-01 before:2026-06-01\n */\n\nexport const SORT_FIELDS = [\"name\", \"size\", \"createdAt\", \"updatedAt\", \"type\"] as const;\nexport type SortField = (typeof SORT_FIELDS)[number];\n\nexport const SORT_ORDERS = [\"asc\", \"desc\"] as const;\nexport type SortOrder = (typeof SORT_ORDERS)[number];\n\nexport const ITEM_STATUSES = [\"active\", \"trashed\", \"all\"] as const;\nexport type ItemStatus = (typeof ITEM_STATUSES)[number];\n\nexport const DEFAULT_PAGE_SIZE = 50;\nexport const MAX_PAGE_SIZE = 200;\n\n/** Query parameters accepted by the file/folder listing endpoints. */\nexport interface ListQuery {\n folderId?: string | null;\n page?: number;\n limit?: number;\n sort?: SortField;\n order?: SortOrder;\n /** MIME type or coarse category (image, video, pdf, document, …). */\n type?: string;\n /** File extension without the dot. */\n ext?: string;\n starred?: boolean;\n status?: ItemStatus;\n /** Name substring filter. */\n q?: string;\n /** Folder-name filter (search parser); resolved to ids by the caller. */\n folderName?: string;\n minSize?: number;\n maxSize?: number;\n /** Inclusive date bounds (ISO strings). */\n from?: string;\n to?: string;\n}\n\nconst SIZE_UNITS: Record<string, number> = {\n b: 1,\n kb: 1024,\n mb: 1024 ** 2,\n gb: 1024 ** 3,\n tb: 1024 ** 4,\n};\n\n/** Parses \"100MB\", \"1.5gb\", \"512\" into bytes. Returns null when unparseable. */\nexport function parseSize(value: string): number | null {\n const match = /^\\s*([0-9]+(?:\\.[0-9]+)?)\\s*([a-zA-Z]*)\\s*$/.exec(value);\n if (!match) return null;\n const amount = Number(match[1]);\n if (!Number.isFinite(amount) || amount < 0) return null;\n const unit = (match[2] ?? \"\").toLowerCase() || \"b\";\n const factor = SIZE_UNITS[unit];\n if (factor === undefined) return null;\n return Math.round(amount * factor);\n}\n\n/** Parses a size comparison such as \">100MB\", \"<=2gb\", \"5mb\". */\nexport function parseSizeComparison(value: string): { min?: number; max?: number } | null {\n const match = /^\\s*(>=|<=|>|<|=)?\\s*(.+)$/.exec(value);\n if (!match) return null;\n const operator = match[1] ?? \"=\";\n const bytes = parseSize(match[2]!);\n if (bytes === null) return null;\n\n switch (operator) {\n case \">\":\n return { min: bytes + 1 };\n case \">=\":\n return { min: bytes };\n case \"<\":\n return { max: bytes - 1 };\n case \"<=\":\n return { max: bytes };\n default:\n return { min: bytes, max: bytes };\n }\n}\n\nfunction parseBoolean(value: string): boolean | null {\n const normalized = value.toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(normalized)) return false;\n return null;\n}\n\nfunction parseDate(value: string, endOfDay: boolean): Date | null {\n const trimmed = value.trim();\n // Plain dates are interpreted as the start (or end) of that UTC day so\n // `after:2026-01-01` and `before:2026-01-01` behave intuitively.\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n const date = new Date(`${trimmed}T${endOfDay ? \"23:59:59.999\" : \"00:00:00.000\"}Z`);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n const date = new Date(trimmed);\n return Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** Parses a raw user query string into structured filters. */\nexport function parseSearchQuery(\n input: string,\n base: ListQuery = {},\n): { query: ListQuery; freeText: string } {\n const query: ListQuery = { ...base };\n const terms: string[] = [];\n\n for (const rawToken of input.trim().split(/\\s+/)) {\n if (!rawToken) continue;\n\n const separator = rawToken.indexOf(\":\");\n if (separator <= 0) {\n terms.push(rawToken);\n continue;\n }\n\n const field = rawToken.slice(0, separator).toLowerCase();\n const value = rawToken.slice(separator + 1);\n if (!value) {\n terms.push(rawToken);\n continue;\n }\n\n switch (field) {\n case \"name\":\n case \"filename\":\n terms.push(value);\n break;\n case \"type\":\n // Accept either a MIME type (\"application/pdf\") or a coarse category.\n query.type = value.toLowerCase();\n break;\n case \"ext\":\n case \"extension\":\n query.ext = value.replace(/^\\./, \"\").toLowerCase();\n break;\n case \"size\": {\n const comparison = parseSizeComparison(value);\n if (!comparison) {\n terms.push(rawToken);\n break;\n }\n if (comparison.min !== undefined) query.minSize = comparison.min;\n if (comparison.max !== undefined) query.maxSize = comparison.max;\n break;\n }\n case \"folder\":\n query.folderName = value;\n break;\n case \"starred\": {\n const parsed = parseBoolean(value);\n if (parsed === null) terms.push(rawToken);\n else query.starred = parsed;\n break;\n }\n case \"trashed\":\n case \"deleted\": {\n const parsed = parseBoolean(value);\n if (parsed === null) terms.push(rawToken);\n else query.status = parsed ? \"trashed\" : \"active\";\n break;\n }\n case \"after\":\n case \"since\": {\n const date = parseDate(value, false);\n if (!date) terms.push(rawToken);\n else query.from = date.toISOString();\n break;\n }\n case \"before\": {\n const date = parseDate(value, true);\n if (!date) terms.push(rawToken);\n else query.to = date.toISOString();\n break;\n }\n default:\n terms.push(rawToken);\n break;\n }\n }\n\n const freeText = terms.join(\" \").trim();\n if (freeText) query.q = freeText;\n return { query, freeText };\n}\n\nexport function normalizePage(value: unknown): number {\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed < 1) return 1;\n return parsed;\n}\n\nexport function normalizeLimit(value: unknown): number {\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed < 1) return DEFAULT_PAGE_SIZE;\n return Math.min(parsed, MAX_PAGE_SIZE);\n}\n\nexport function normalizeSort(value: unknown): SortField | undefined {\n if (typeof value !== \"string\") return undefined;\n return (SORT_FIELDS as readonly string[]).includes(value) ? (value as SortField) : undefined;\n}\n\nexport function normalizeOrder(value: unknown): SortOrder | undefined {\n if (typeof value !== \"string\") return undefined;\n return (SORT_ORDERS as readonly string[]).includes(value) ? (value as SortOrder) : undefined;\n}\n\n/** Builds the pagination envelope for a result page. */\nexport function paginationMeta(page: number, limit: number, total: number) {\n return { page, limit, total, hasMore: page * limit < total };\n}\n","/**\n * Data transfer objects, constants and error codes shared between the API,\n * the web app, and the @lacrous/omnicloud SDK.\n *\n * Telegram identifiers that may exceed JavaScript's safe integer range are\n * transported as strings across the API boundary.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Identity & storage\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface UserDTO {\n id: string;\n username: string | null;\n firstName: string | null;\n lastName: string | null;\n}\n\nexport interface StorageDTO {\n id: string;\n provider: string;\n title: string;\n createdAt: string;\n}\n\n/** Connection state of the underlying storage backend. */\nexport type ConnectionState =\n \"DISCONNECTED\" | \"CONNECTING\" | \"CONNECTED\" | \"RECONNECTING\" | \"ERROR\" | \"AUTH_REQUIRED\";\n\nexport type HealthStatus = \"healthy\" | \"degraded\" | \"unavailable\" | \"unknown\";\n\nexport interface StorageHealthDTO {\n provider: string;\n state: ConnectionState;\n status: HealthStatus;\n latencyMs: number | null;\n message: string | null;\n channelTitle: string | null;\n checkedAt: string;\n}\n\n/** Returned by GET /api/auth/me. `user` is null when not signed in. */\nexport interface SessionInfo {\n user: UserDTO | null;\n storage: StorageDTO | null;\n health: StorageHealthDTO | null;\n}\n\n/** POST /api/auth/telegram/verify response. */\nexport type TelegramVerifyResponse =\n { status: \"ok\"; user: UserDTO } | { status: \"password_required\" };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Files & folders\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface FileDTO {\n id: string;\n name: string;\n size: number;\n mimeType: string;\n sha256: string;\n folderId: string | null;\n starred: boolean;\n trashed: boolean;\n deletedAt: string | null;\n versionCount: number;\n currentVersionId: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface FolderDTO {\n id: string;\n name: string;\n parentId: string | null;\n starred: boolean;\n trashed: boolean;\n deletedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface FileVersionDTO {\n id: string;\n fileId: string;\n versionNumber: number;\n size: number;\n mimeType: string;\n sha256: string;\n telegramMessageId: number;\n isCurrent: boolean;\n createdAt: string;\n}\n\nexport interface SearchResultDTO {\n files: FileDTO[];\n folders: FolderDTO[];\n pagination: PaginationDTO;\n}\n\n/** Result of a recursive folder deletion / trash operation. */\nexport interface FolderMutationResultDTO {\n affectedFolders: number;\n affectedFiles: number;\n}\n\nexport interface EmptyTrashResultDTO {\n deletedFiles: number;\n deletedFolders: number;\n failedFiles: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Pagination, sorting & filtering\n// ─────────────────────────────────────────────────────────────────────────────\n\nimport type { ListQuery } from \"./query\";\n\nexport type { ListQuery, SortField, SortOrder, ItemStatus } from \"./query\";\n\nexport interface PaginationDTO {\n page: number;\n limit: number;\n total: number;\n hasMore: boolean;\n}\n\nexport interface FilesPageDTO {\n files: FileDTO[];\n pagination: PaginationDTO;\n}\n\nexport interface FoldersPageDTO {\n folders: FolderDTO[];\n pagination: PaginationDTO;\n}\n\nexport interface TrashPageDTO {\n files: FileDTO[];\n folders: FolderDTO[];\n pagination: PaginationDTO;\n}\n\n/** Alias so callers can name the query shape as query parameters. */\nexport type QueryParams = ListQuery;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Recent & activity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RecentItemDTO {\n file: FileDTO;\n lastAction: ActivityAction;\n lastActionAt: string;\n}\n\nexport interface RecentPageDTO {\n items: RecentItemDTO[];\n pagination: PaginationDTO;\n}\n\nexport const ACTIVITY_ACTIONS = [\n \"upload\",\n \"download\",\n \"open\",\n \"rename\",\n \"move\",\n \"star\",\n \"unstar\",\n \"trash\",\n \"restore\",\n \"delete\",\n \"create_folder\",\n \"replace\",\n] as const;\nexport type ActivityAction = (typeof ACTIVITY_ACTIONS)[number];\n\nexport const RESOURCE_TYPES = [\"file\", \"folder\", \"storage\"] as const;\nexport type ResourceType = (typeof RESOURCE_TYPES)[number];\n\nexport interface ActivityEventDTO {\n id: string;\n action: ActivityAction;\n resourceType: ResourceType;\n resourceId: string;\n resourceName: string | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n}\n\nexport interface ActivityPageDTO {\n events: ActivityEventDTO[];\n pagination: PaginationDTO;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Storage statistics\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface StorageTypeBreakdownDTO {\n category: string;\n bytes: number;\n count: number;\n}\n\nexport interface StorageStatsDTO {\n fileCount: number;\n folderCount: number;\n totalBytes: number;\n trashBytes: number;\n trashFileCount: number;\n starredCount: number;\n byType: StorageTypeBreakdownDTO[];\n largestFiles: FileDTO[];\n quotaBytes: number | null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Health & integrity\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface HealthDTO {\n status: HealthStatus;\n database: HealthStatus;\n storage: HealthStatus;\n uptimeSeconds: number;\n version: string;\n}\n\nexport interface IntegrityIssueDTO {\n fileId: string;\n name: string;\n kind: \"missing\" | \"size_mismatch\" | \"unreadable\" | \"hash_mismatch\";\n detail: string | null;\n}\n\nexport interface IntegrityReportDTO {\n checkedAt: string;\n filesChecked: number;\n healthy: number;\n missing: number;\n inconsistent: number;\n unreadable: number;\n issues: IntegrityIssueDTO[];\n durationMs: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Batch operations\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const BATCH_FILE_OPERATIONS = [\n \"trash\",\n \"restore\",\n \"delete\",\n \"star\",\n \"unstar\",\n \"move\",\n] as const;\nexport type BatchFileOperation = (typeof BATCH_FILE_OPERATIONS)[number];\n\nexport const BATCH_FOLDER_OPERATIONS = [\n \"trash\",\n \"restore\",\n \"delete\",\n \"star\",\n \"unstar\",\n \"move\",\n] as const;\nexport type BatchFolderOperation = (typeof BATCH_FOLDER_OPERATIONS)[number];\n\nexport interface BatchResultDTO {\n requested: number;\n succeeded: number;\n failed: number;\n errors: { id: string; code: ErrorCode; message: string }[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Errors\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Stable machine-readable error codes returned in API error bodies. */\nexport const ERROR_CODES = {\n AUTH_REQUIRED: \"AUTH_REQUIRED\",\n AUTH_INVALID: \"AUTH_INVALID\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n INVALID_REQUEST: \"INVALID_REQUEST\",\n NOT_FOUND: \"NOT_FOUND\",\n FILE_NOT_FOUND: \"FILE_NOT_FOUND\",\n FOLDER_NOT_FOUND: \"FOLDER_NOT_FOUND\",\n CONFLICT: \"CONFLICT\",\n PAYLOAD_TOO_LARGE: \"PAYLOAD_TOO_LARGE\",\n RATE_LIMITED: \"RATE_LIMITED\",\n STORAGE_NOT_INITIALIZED: \"STORAGE_NOT_INITIALIZED\",\n STORAGE_UNAVAILABLE: \"STORAGE_UNAVAILABLE\",\n TELEGRAM_AUTH_REQUIRED: \"TELEGRAM_AUTH_REQUIRED\",\n TELEGRAM_CONNECTION_FAILED: \"TELEGRAM_CONNECTION_FAILED\",\n TELEGRAM_FILE_NOT_FOUND: \"TELEGRAM_FILE_NOT_FOUND\",\n UPLOAD_FAILED: \"UPLOAD_FAILED\",\n DOWNLOAD_FAILED: \"DOWNLOAD_FAILED\",\n INTEGRITY_CHECK_FAILED: \"INTEGRITY_CHECK_FAILED\",\n INTERNAL: \"INTERNAL_ERROR\",\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\nexport interface ApiErrorBody {\n error: {\n code: ErrorCode;\n message: string;\n details?: unknown;\n requestId?: string;\n };\n}\n\nexport const SESSION_COOKIE = \"omnicloud_session\";\nexport const REQUEST_ID_HEADER = \"x-request-id\";\n\nexport * from \"./mime\";\nexport * from \"./query\";\n","import { ERROR_CODES } from \"@omnicloud/shared\";\nimport type { ErrorCode } from \"@omnicloud/shared\";\n\n/**\n * Base class for all domain errors. The API maps these to structured HTTP\n * responses; everything else becomes a 500.\n */\nexport class DomainError extends Error {\n readonly code: ErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ErrorCode, status: number, message: string, details?: unknown) {\n super(message);\n this.name = new.target.name;\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\n/** Invalid request payload — 400. */\nexport class ValidationError extends DomainError {\n constructor(message: string, details?: unknown) {\n super(ERROR_CODES.INVALID_REQUEST, 400, message, details);\n }\n}\n\n/** Missing browser session — 401. */\nexport class UnauthorizedError extends DomainError {\n constructor(message = \"Authentication required\") {\n super(ERROR_CODES.AUTH_REQUIRED, 401, message);\n }\n}\n\n/** Invalid credentials or expired session token — 401. */\nexport class AuthInvalidError extends DomainError {\n constructor(message = \"Your session is no longer valid — please sign in again\") {\n super(ERROR_CODES.AUTH_INVALID, 401, message);\n }\n}\n\n/** Authenticated but not allowed — 403. */\nexport class ForbiddenError extends DomainError {\n constructor(message = \"You do not have access to this resource\") {\n super(ERROR_CODES.PERMISSION_DENIED, 403, message);\n }\n}\n\n/**\n * Used for missing resources as well as resources owned by someone else, to\n * avoid leaking existence. `resource` narrows the error code.\n */\nexport class NotFoundError extends DomainError {\n constructor(message = \"Resource not found\", resource?: \"file\" | \"folder\") {\n super(\n resource === \"file\"\n ? ERROR_CODES.FILE_NOT_FOUND\n : resource === \"folder\"\n ? ERROR_CODES.FOLDER_NOT_FOUND\n : ERROR_CODES.NOT_FOUND,\n 404,\n message,\n );\n }\n}\n\nexport class ConflictError extends DomainError {\n constructor(message: string) {\n super(ERROR_CODES.CONFLICT, 409, message);\n }\n}\n\nexport class PayloadTooLargeError extends DomainError {\n constructor(message = \"File is too large\") {\n super(ERROR_CODES.PAYLOAD_TOO_LARGE, 413, message);\n }\n}\n\nexport class RateLimitedError extends DomainError {\n constructor(message = \"Too many requests, please retry later\") {\n super(ERROR_CODES.RATE_LIMITED, 429, message);\n }\n}\n\n/** The user has no storage backend set up yet — 409. */\nexport class StorageNotInitializedError extends DomainError {\n constructor() {\n super(ERROR_CODES.STORAGE_NOT_INITIALIZED, 409, \"Storage is not initialized for this account\");\n }\n}\n\n/** The storage backend is reachable but failing — 503. */\nexport class StorageUnavailableError extends DomainError {\n constructor(message = \"Storage is temporarily unavailable\", details?: unknown) {\n super(ERROR_CODES.STORAGE_UNAVAILABLE, 503, message, details);\n }\n}\n\n/** Telegram needs re-authentication — 401. */\nexport class TelegramAuthRequiredError extends DomainError {\n constructor(message = \"Your Telegram session has expired — please reconnect\") {\n super(ERROR_CODES.TELEGRAM_AUTH_REQUIRED, 401, message);\n }\n}\n\n/** Telegram connection could not be established — 502. */\nexport class TelegramConnectionError extends DomainError {\n constructor(message = \"Could not reach Telegram\", details?: unknown) {\n super(ERROR_CODES.TELEGRAM_CONNECTION_FAILED, 502, message, details);\n }\n}\n\n/** The stored object is missing from Telegram — 404. */\nexport class TelegramFileNotFoundError extends DomainError {\n constructor(message = \"The stored file is missing from Telegram\") {\n super(ERROR_CODES.TELEGRAM_FILE_NOT_FOUND, 404, message);\n }\n}\n\n/** Upload failed — 502. */\nexport class UploadFailedError extends DomainError {\n constructor(message = \"Upload failed\", details?: unknown) {\n super(ERROR_CODES.UPLOAD_FAILED, 502, message, details);\n }\n}\n\n/** Download failed — 502. */\nexport class DownloadFailedError extends DomainError {\n constructor(message = \"Download failed\", details?: unknown) {\n super(ERROR_CODES.DOWNLOAD_FAILED, 502, message, details);\n }\n}\n\n/** Integrity verification failed — 502. */\nexport class IntegrityCheckError extends DomainError {\n constructor(message = \"Integrity check failed\", details?: unknown) {\n super(ERROR_CODES.INTEGRITY_CHECK_FAILED, 502, message, details);\n }\n}\n\n/** Operation cancelled by the client — 499 (non-standard but conventional). */\nexport class OperationCancelledError extends DomainError {\n constructor(message = \"Operation cancelled\") {\n super(ERROR_CODES.INVALID_REQUEST, 499, message);\n }\n}\n\n/** @deprecated Use the specific storage errors. Kept for provider compatibility. */\nexport class StorageProviderError extends DomainError {\n constructor(message: string, details?: unknown) {\n super(ERROR_CODES.TELEGRAM_CONNECTION_FAILED, 502, message, details);\n }\n}\n\n/** True when the value is one of our structured domain errors. */\nexport function isDomainError(error: unknown): error is DomainError {\n return error instanceof DomainError;\n}\n\n/**\n * Wraps an arbitrary provider failure as an UploadFailedError (the default\n * storage-operation error), preserving domain errors untouched.\n */\nexport function mapProviderError(message: string, error: unknown): DomainError {\n if (error instanceof DomainError) return error;\n const detail = error instanceof Error ? error.message : String(error);\n return new UploadFailedError(detail ? `${message}: ${detail}` : message, error);\n}\n","import { Readable } from \"node:stream\";\nimport type {\n StorageHealth,\n StorageProvider,\n StorageUploadInput,\n StoredObject,\n StoredRef,\n TransferControl,\n} from \"./provider\";\nimport { OperationCancelledError, UploadFailedError, mapProviderError } from \"../errors\";\n\nexport { sha256Hex } from \"../utils/hash\";\nimport { sha256Hex } from \"../utils/hash\";\n\nexport interface EngineUploadResult {\n stored: StoredObject;\n sha256: string;\n size: number;\n}\n\nexport interface EngineRetryPolicy {\n /** Total attempts, including the first. */\n attempts: number;\n /** Base delay in ms; doubles per retry. */\n baseDelayMs: number;\n}\n\nexport const DEFAULT_RETRY_POLICY: EngineRetryPolicy = { attempts: 3, baseDelayMs: 400 };\n\n/** Errors that are worth retrying (transient transport/rate-limit issues). */\nfunction isRetryable(error: unknown): boolean {\n if (error instanceof OperationCancelledError) return false;\n const name = error instanceof Error ? error.name : \"\";\n const message = error instanceof Error ? error.message : String(error);\n const code = (error as { code?: string }).code;\n // Abort/explicit cancellation never retries.\n if (name === \"AbortError\" || code === \"ABORT_ERR\") return false;\n // Auth/validation problems will not fix themselves.\n if (/AUTH|PASSWORD|PHONE|FLOOD_WAIT|not found|invalid/i.test(message)) return false;\n return true;\n}\n\nfunction delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new OperationCancelledError());\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n function onAbort() {\n clearTimeout(timer);\n reject(new OperationCancelledError());\n }\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw new OperationCancelledError();\n}\n\n/** Converts a Buffer into a single-chunk Readable. */\nfunction bufferToStream(data: Buffer): Readable {\n return Readable.from([data]);\n}\n\n/**\n * Sits between the application and the raw StorageProvider and adds the\n * cross-cutting guarantees: SHA-256 integrity, streaming, progress reporting,\n * cancellation and bounded retries.\n *\n * The active provider is chosen per user by the host application (each user\n * has their own Telegram storage channel).\n */\nexport class StorageEngine {\n constructor(\n private readonly provider: StorageProvider,\n private readonly retryPolicy: EngineRetryPolicy = DEFAULT_RETRY_POLICY,\n ) {}\n\n get providerName(): string {\n return this.provider.name;\n }\n\n /**\n * Uploads an object, computing its SHA-256 as it goes and reporting\n * progress. Retries transient failures with exponential backoff.\n */\n async upload(\n input: StorageUploadInput,\n control: TransferControl = {},\n ): Promise<EngineUploadResult> {\n const data = await this.readInput(input);\n\n const sha256 = sha256Hex(data);\n control.onProgress?.({ transferred: 0, total: data.byteLength, percent: 0 });\n\n const stored = await this.withRetry(\n () => this.provider.put({ ...input, data }, control),\n control,\n (error) => new UploadFailedError(\"Upload failed\", error),\n );\n\n control.onProgress?.({ transferred: data.byteLength, total: data.byteLength, percent: 100 });\n return { stored, sha256, size: data.byteLength };\n }\n\n async download(ref: StoredRef, control: TransferControl = {}): Promise<Buffer> {\n return this.withRetry(\n () => this.provider.get(ref, control),\n control,\n (error) => mapProviderError(\"Download failed\", error),\n );\n }\n\n /** Streams the object, computing the checksum on the fly when requested. */\n async downloadStream(\n ref: StoredRef,\n control: TransferControl = {},\n ): Promise<{ stream: Readable; hash: () => string | null }> {\n const stream = await this.withRetry(\n () => this.provider.getStream(ref, control),\n control,\n (error) => mapProviderError(\"Download failed\", error),\n );\n return { stream, hash: () => null };\n }\n\n async remove(ref: StoredRef): Promise<void> {\n await this.withRetry(\n () => this.provider.delete(ref),\n {},\n (error) => mapProviderError(\"Delete failed\", error),\n );\n }\n\n async exists(ref: StoredRef): Promise<boolean> {\n try {\n return await this.provider.exists(ref);\n } catch (error) {\n throw mapProviderError(\"Storage lookup failed\", error);\n }\n }\n\n async stat(ref: StoredRef): Promise<StoredObject | null> {\n try {\n return await this.provider.stat(ref);\n } catch (error) {\n throw mapProviderError(\"Storage lookup failed\", error);\n }\n }\n\n async health(): Promise<StorageHealth> {\n try {\n return await this.provider.healthCheck();\n } catch (error) {\n return {\n healthy: false,\n latencyMs: null,\n message: error instanceof Error ? error.message : \"Storage health check failed\",\n targetTitle: null,\n };\n }\n }\n\n async verifyIntegrity(data: Buffer, expectedSha256: string): Promise<boolean> {\n return sha256Hex(data) === expectedSha256;\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n /** Materializes an upload input so the checksum can be computed. */\n private async readInput(input: StorageUploadInput): Promise<Buffer> {\n if (input.data) return input.data;\n if (!input.stream) {\n throw new UploadFailedError(\"Upload input has neither data nor a stream\");\n }\n const chunks: Buffer[] = [];\n for await (const chunk of input.stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as Uint8Array));\n }\n return Buffer.concat(chunks);\n }\n\n private async withRetry<T>(\n operation: () => Promise<T>,\n control: TransferControl,\n wrapError: (error: unknown) => Error,\n ): Promise<T> {\n const { attempts, baseDelayMs } = this.retryPolicy;\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= Math.max(1, attempts); attempt += 1) {\n throwIfAborted(control.signal);\n try {\n return await operation();\n } catch (error) {\n lastError = error;\n if (attempt >= attempts || !isRetryable(error)) break;\n await delay(baseDelayMs * 2 ** (attempt - 1), control.signal);\n }\n }\n throw wrapError(lastError);\n }\n}\n\nexport { bufferToStream };\n","import { createHash } from \"node:crypto\";\n\nexport function sha256Hex(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n","import { mimeMatchersForType, paginationMeta } from \"@omnicloud/shared\";\nimport type { ListQuery, PaginationDTO } from \"@omnicloud/shared\";\nimport type { FolderRepository } from \"../repos\";\nimport type { ItemQuery } from \"../types\";\n\n/**\n * Translates the shared, user-facing ListQuery into the repository-ready\n * ItemQuery: validates/normalizes sort and date bounds, expands MIME\n * categories, and resolves `folder:Name` filters to concrete folder ids.\n */\nexport async function resolveItemQuery(\n folders: FolderRepository,\n userId: string,\n input: ListQuery,\n): Promise<ItemQuery> {\n const query: ItemQuery = {};\n\n // `folderId: null` means root; `undefined` means \"any folder\". Distinguish\n // them by presence of a defined value rather than the key itself.\n if (input.folderId !== undefined) query.folderId = input.folderId;\n if (input.q && input.q.trim()) query.nameContains = input.q.trim();\n if (input.ext) query.ext = input.ext.replace(/^\\./, \"\").toLowerCase();\n if (input.starred !== undefined) query.starred = input.starred;\n if (input.minSize !== undefined) query.minSize = input.minSize;\n if (input.maxSize !== undefined) query.maxSize = input.maxSize;\n if (input.sort) query.sort = input.sort;\n if (input.order) query.order = input.order;\n\n switch (input.status) {\n case \"active\":\n query.trashed = false;\n break;\n case \"trashed\":\n query.trashed = true;\n break;\n default:\n query.trashed = undefined;\n break;\n }\n\n if (input.type) {\n const matchers = mimeMatchersForType(input.type);\n if (matchers) query.mimeMatchers = matchers;\n }\n\n if (input.from) {\n const from = new Date(input.from);\n if (!Number.isNaN(from.getTime())) query.from = from;\n }\n if (input.to) {\n const to = new Date(input.to);\n if (!Number.isNaN(to.getTime())) query.to = to;\n }\n\n if (input.folderName) {\n query.folderIds = await folders.findIdsByName(userId, input.folderName);\n }\n\n return query;\n}\n\n/** Builds the pagination envelope shared by every listing endpoint. */\nexport function toPagination(page: number, limit: number, total: number): PaginationDTO {\n return paginationMeta(page, limit, total);\n}\n\nexport { paginationMeta };\n","import type { ActivityAction, ResourceType } from \"@omnicloud/shared\";\nimport type { ActivityRepository } from \"../repos\";\nimport type { ActivityEventRecord, PageRequest, Paged } from \"../types\";\n\nexport interface RecordActivityInput {\n userId: string;\n action: ActivityAction;\n resourceType: ResourceType;\n resourceId: string;\n resourceName?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\n/**\n * The narrow interface other services depend on for activity logging, so a\n * no-op recorder can be substituted in tests and activity failures never\n * break the operation being logged.\n */\nexport interface ActivityRecorder {\n record(input: RecordActivityInput): Promise<void>;\n}\n\n/** Activity log with best-effort recording and retention pruning. */\nexport class ActivityService implements ActivityRecorder {\n constructor(\n private readonly activity: ActivityRepository,\n /** Days to retain events; older rows are pruned on demand. */\n private readonly retentionDays = 90,\n ) {}\n\n /**\n * Records an event. Never throws — a logging failure must not fail the\n * user-facing operation that produced it.\n */\n async record(input: RecordActivityInput): Promise<void> {\n try {\n await this.activity.record({\n userId: input.userId,\n action: input.action,\n resourceType: input.resourceType,\n resourceId: input.resourceId,\n resourceName: input.resourceName ?? null,\n metadata: sanitizeMetadata(input.metadata),\n });\n } catch {\n // Swallow: activity logging is observability, not a transaction.\n }\n }\n\n async list(userId: string, page: PageRequest): Promise<Paged<ActivityEventRecord>> {\n return this.activity.list(userId, page);\n }\n\n /** Most recent distinct files for the given actions, for the Recent view. */\n async recentFiles(\n userId: string,\n actions: readonly ActivityAction[],\n limit: number,\n ): Promise<ActivityEventRecord[]> {\n return this.activity.recentFiles(userId, actions, limit);\n }\n\n async prune(): Promise<number> {\n const cutoff = new Date(Date.now() - this.retentionDays * 24 * 60 * 60 * 1000);\n return this.activity.pruneOlderThan(cutoff);\n }\n}\n\nconst SENSITIVE_KEY = /(password|secret|token|session|hash|credential)/i;\n\n/**\n * Strips credential-shaped keys so the activity log can never become a place\n * where secrets leak.\n */\nfunction sanitizeMetadata(\n metadata: Record<string, unknown> | null | undefined,\n): Record<string, unknown> | null {\n if (!metadata) return null;\n const clean: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (SENSITIVE_KEY.test(key)) continue;\n if (value === undefined) continue;\n clean[key] = value;\n }\n return Object.keys(clean).length > 0 ? clean : null;\n}\n\n/** A recorder that discards events (used where activity is not wired up). */\nexport const noopActivityRecorder: ActivityRecorder = {\n async record() {\n /* intentionally empty */\n },\n};\n","/**\n * Sanitizes a client-supplied filename into a safe, single path component.\n * Returns an empty string when nothing usable remains.\n */\nexport function sanitizeFileName(name: string): string {\n // Drop any path components — prevents path traversal.\n const base = name.split(/[/\\\\]/).pop() ?? name;\n // Strip control characters and characters that are unsafe across filesystems.\n const cleaned = base\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f]/g, \"\")\n .replace(/[<>:\"|?*]/g, \"_\")\n .trim();\n if (!cleaned || cleaned === \".\" || cleaned === \"..\") return \"\";\n return cleaned.slice(0, 255);\n}\n","import type { ListQuery } from \"@omnicloud/shared\";\nimport { mimeFromFilename } from \"@omnicloud/shared\";\nimport { ConflictError, NotFoundError, ValidationError } from \"../errors\";\nimport { sanitizeFileName } from \"../utils/filename\";\nimport type { FileRecord, FileVersionRecord, ItemQuery, PageRequest, Paged } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\nimport type { StorageEngine } from \"../storage/engine\";\nimport type { TransferControl, TransferProgress } from \"../storage/provider\";\nimport type { ActivityRecorder } from \"./activity-service\";\nimport { noopActivityRecorder } from \"./activity-service\";\nimport { resolveItemQuery } from \"./query-resolver\";\n\nexport interface FileDownload {\n record: FileRecord;\n data: Buffer;\n integrityVerified: boolean;\n}\n\nexport type EngineResolver = (userId: string) => Promise<StorageEngine>;\n\nexport interface FileUploadInput {\n folderId: string | null;\n name: string;\n data: Buffer;\n /** When set, uploads a new version of this file instead of creating one. */\n replaceFileId?: string;\n /** Progress callback forwarded from the transport layer. */\n onProgress?: (progress: TransferProgress) => void;\n signal?: AbortSignal;\n}\n\nexport interface FileListResult {\n items: FileRecord[];\n total: number;\n}\n\n/**\n * File metadata operations. Every method verifies ownership: records\n * belonging to other users are reported as missing.\n *\n * Reliability rules honored here:\n * - metadata is written only after the storage upload succeeds;\n * - permanent deletion removes the remote object before the metadata;\n * - client-supplied ownership is never trusted (userId is server-derived).\n */\nexport class FileService {\n constructor(\n private readonly files: FileRepository,\n private readonly folders: FolderRepository,\n private readonly engineFor: EngineResolver,\n private readonly activity: ActivityRecorder = noopActivityRecorder,\n ) {}\n\n // ── Upload ───────────────────────────────────────────────────────────────\n\n async upload(userId: string, input: FileUploadInput): Promise<FileRecord> {\n const name = sanitizeFileName(input.name);\n if (!name) throw new ValidationError(\"Invalid file name\");\n\n let folderId = input.folderId;\n if (input.replaceFileId) {\n const existing = await this.get(userId, input.replaceFileId);\n folderId = existing.folderId;\n } else if (folderId !== null) {\n await this.assertFolder(userId, folderId);\n }\n\n const mimeType = mimeFromFilename(name);\n const engine = await this.engineFor(userId);\n\n const control: TransferControl = {\n onProgress: input.onProgress,\n signal: input.signal,\n };\n\n // Upload first; metadata is only persisted once the provider succeeds.\n const { stored, sha256, size } = await engine.upload(\n { name, mimeType, data: input.data },\n control,\n );\n const telegramMessageId = Number(stored.messageId);\n\n if (input.replaceFileId) {\n return this.replaceVersion(userId, input.replaceFileId, {\n name,\n size,\n mimeType,\n sha256,\n telegramMessageId,\n });\n }\n\n const record = await this.files.create({\n userId,\n folderId,\n name,\n size,\n mimeType,\n sha256,\n telegramMessageId,\n });\n\n // Establish version 1 immediately so the version history is complete from\n // the first upload (the foundation for v0.3's version UI).\n const version = await this.files.createVersion({\n fileId: record.id,\n userId,\n size,\n mimeType,\n sha256,\n telegramMessageId,\n });\n const withVersion = await this.files.update(record.id, {\n currentVersionId: version.id,\n versionCount: 1,\n });\n\n await this.activity.record({\n userId,\n action: \"upload\",\n resourceType: \"file\",\n resourceId: withVersion.id,\n resourceName: withVersion.name,\n metadata: { size, mimeType },\n });\n\n return withVersion;\n }\n\n /** Uploads a new version of an existing file and makes it current. */\n private async replaceVersion(\n userId: string,\n fileId: string,\n next: {\n name: string;\n size: number;\n mimeType: string;\n sha256: string;\n telegramMessageId: number;\n },\n ): Promise<FileRecord> {\n const existing = await this.get(userId, fileId);\n\n const version = await this.files.createVersion({\n fileId,\n userId,\n size: next.size,\n mimeType: next.mimeType,\n sha256: next.sha256,\n telegramMessageId: next.telegramMessageId,\n });\n const count = await this.files.countVersions(fileId);\n\n const updated = await this.files.update(fileId, {\n name: next.name,\n size: next.size,\n mimeType: next.mimeType,\n sha256: next.sha256,\n telegramMessageId: next.telegramMessageId,\n currentVersionId: version.id,\n versionCount: count,\n });\n\n await this.activity.record({\n userId,\n action: \"replace\",\n resourceType: \"file\",\n resourceId: fileId,\n resourceName: updated.name,\n metadata: { previousSha256: existing.sha256, size: next.size },\n });\n\n return updated;\n }\n\n // ── Reads ────────────────────────────────────────────────────────────────\n\n async list(userId: string, folderId: string | null): Promise<FileRecord[]> {\n if (folderId !== null) await this.assertFolder(userId, folderId);\n return this.files.listByFolder(userId, folderId);\n }\n\n async query(userId: string, input: ListQuery, page: PageRequest): Promise<Paged<FileRecord>> {\n if (input.folderId != null) await this.assertFolder(userId, input.folderId);\n const query: ItemQuery = await resolveItemQuery(this.folders, userId, input);\n return this.files.query(userId, query, page);\n }\n\n async get(userId: string, id: string): Promise<FileRecord> {\n const record = await this.files.findById(id);\n if (!record || record.userId !== userId) throw new NotFoundError(\"File not found\", \"file\");\n return record;\n }\n\n /** Like get(), but rejects trashed files (for download/open). */\n async getActive(userId: string, id: string): Promise<FileRecord> {\n const record = await this.get(userId, id);\n if (record.deletedAt) throw new NotFoundError(\"File not found\", \"file\");\n return record;\n }\n\n async listVersions(userId: string, id: string): Promise<FileVersionRecord[]> {\n await this.get(userId, id);\n return this.files.listVersions(id);\n }\n\n // ── Mutations ────────────────────────────────────────────────────────────\n\n async rename(userId: string, id: string, name: string): Promise<FileRecord> {\n const existing = await this.get(userId, id);\n const clean = sanitizeFileName(name);\n if (!clean) throw new ValidationError(\"Invalid file name\");\n const updated = await this.files.update(id, { name: clean });\n await this.activity.record({\n userId,\n action: \"rename\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: clean,\n metadata: { previousName: existing.name },\n });\n return updated;\n }\n\n async move(userId: string, id: string, folderId: string | null): Promise<FileRecord> {\n const existing = await this.get(userId, id);\n if (folderId !== null) await this.assertFolder(userId, folderId);\n const updated = await this.files.update(id, { folderId });\n await this.activity.record({\n userId,\n action: \"move\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: updated.name,\n metadata: { from: existing.folderId, to: folderId },\n });\n return updated;\n }\n\n async setStarred(userId: string, id: string, starred: boolean): Promise<FileRecord> {\n await this.get(userId, id);\n const updated = await this.files.update(id, { starred });\n await this.activity.record({\n userId,\n action: starred ? \"star\" : \"unstar\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: updated.name,\n });\n return updated;\n }\n\n /**\n * Moves a file to Trash (soft delete). The remote object is untouched, so\n * restore is lossless.\n */\n async trash(userId: string, id: string): Promise<FileRecord> {\n const record = await this.get(userId, id);\n if (record.deletedAt) return record;\n const updated = await this.files.update(id, {\n deletedAt: new Date(),\n trashBatchId: `solo-${id}`,\n });\n await this.activity.record({\n userId,\n action: \"trash\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: updated.name,\n });\n return updated;\n }\n\n /** Restores a trashed file to its previous folder (or root). */\n async restore(userId: string, id: string): Promise<FileRecord> {\n const record = await this.get(userId, id);\n\n // If the original folder is gone or itself trashed, fall back to root so a\n // restore can never produce an orphaned file.\n let folderId = record.folderId;\n if (folderId !== null) {\n const folder = await this.folders.findById(folderId);\n if (!folder || folder.userId !== userId || folder.deletedAt) folderId = null;\n }\n\n const updated = await this.files.update(id, { deletedAt: null, trashBatchId: null, folderId });\n await this.activity.record({\n userId,\n action: \"restore\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: updated.name,\n });\n return updated;\n }\n\n /**\n * Permanently deletes a file: the Telegram object is removed first, then the\n * metadata. If the remote delete fails we keep the metadata so the user can\n * retry, rather than silently orphaning the storage object.\n */\n async deletePermanently(userId: string, id: string): Promise<void> {\n const record = await this.get(userId, id);\n const engine = await this.engineFor(userId);\n\n await engine.remove({ messageId: String(record.telegramMessageId) });\n // Old versions may hold separate remote objects; clean them up too.\n const versions = await this.files.listVersions(id);\n for (const version of versions) {\n if (version.telegramMessageId === record.telegramMessageId) continue;\n try {\n await engine.remove({ messageId: String(version.telegramMessageId) });\n } catch {\n // Best effort — a leftover message is harmless once metadata is gone.\n }\n }\n\n await this.files.deleteVersionsByFileIds([id]);\n await this.files.deleteMany([id]);\n await this.activity.record({\n userId,\n action: \"delete\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: record.name,\n });\n }\n\n /**\n * v0.1-compatible hard delete used by the current API: permanently removes\n * the file. Retained so existing clients keep working.\n */\n async delete(userId: string, id: string): Promise<void> {\n await this.deletePermanently(userId, id);\n }\n\n // ── Download ─────────────────────────────────────────────────────────────\n\n async download(userId: string, id: string, control: TransferControl = {}): Promise<FileDownload> {\n const record = await this.getActive(userId, id);\n const engine = await this.engineFor(userId);\n const data = await engine.download({ messageId: String(record.telegramMessageId) }, control);\n const integrityVerified = await engine.verifyIntegrity(data, record.sha256);\n await this.activity.record({\n userId,\n action: \"download\",\n resourceType: \"file\",\n resourceId: id,\n resourceName: record.name,\n metadata: { size: record.size, integrityVerified },\n });\n return { record, data, integrityVerified };\n }\n\n /** Records an \"open\" event (the UI opening the details panel, say). */\n async touch(userId: string, id: string, action: \"open\" = \"open\"): Promise<void> {\n const record = await this.get(userId, id);\n await this.activity.record({\n userId,\n action,\n resourceType: \"file\",\n resourceId: id,\n resourceName: record.name,\n });\n }\n\n // ── Batch operations ─────────────────────────────────────────────────────\n\n /**\n * Applies an operation to many files, collecting per-item failures instead\n * of aborting the whole batch.\n */\n async batch(\n userId: string,\n ids: string[],\n operation: \"trash\" | \"restore\" | \"delete\" | \"star\" | \"unstar\" | \"move\",\n options: { folderId?: string | null } = {},\n ): Promise<{\n requested: number;\n succeeded: number;\n failed: number;\n errors: { id: string; message: string }[];\n }> {\n const errors: { id: string; message: string }[] = [];\n let succeeded = 0;\n\n if (ids.length === 0) throw new ValidationError(\"No items selected\");\n\n // Validate the destination once, so a bad folder fails the whole batch\n // rather than producing N identical errors.\n if (operation === \"move\") {\n const target = options.folderId ?? null;\n if (target !== null) await this.assertFolder(userId, target);\n }\n\n for (const id of ids) {\n try {\n switch (operation) {\n case \"trash\":\n await this.trash(userId, id);\n break;\n case \"restore\":\n await this.restore(userId, id);\n break;\n case \"delete\":\n await this.deletePermanently(userId, id);\n break;\n case \"star\":\n await this.setStarred(userId, id, true);\n break;\n case \"unstar\":\n await this.setStarred(userId, id, false);\n break;\n case \"move\":\n await this.move(userId, id, options.folderId ?? null);\n break;\n }\n succeeded += 1;\n } catch (error) {\n errors.push({ id, message: error instanceof Error ? error.message : \"Operation failed\" });\n }\n }\n\n return { requested: ids.length, succeeded, failed: errors.length, errors };\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private async assertFolder(userId: string, folderId: string): Promise<void> {\n const folder = await this.folders.findById(folderId);\n if (!folder || folder.userId !== userId) {\n throw new NotFoundError(\"Folder not found\", \"folder\");\n }\n if (folder.deletedAt) throw new ConflictError(\"The target folder is in the Trash\");\n }\n}\n","import type { ListQuery } from \"@omnicloud/shared\";\nimport { ConflictError, NotFoundError, ValidationError } from \"../errors\";\nimport { sanitizeFileName } from \"../utils/filename\";\nimport type { FolderRecord, ItemQuery, PageRequest, Paged } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\nimport type { EngineResolver } from \"./file-service\";\nimport type { ActivityRecorder } from \"./activity-service\";\nimport { noopActivityRecorder } from \"./activity-service\";\nimport { resolveItemQuery } from \"./query-resolver\";\n\nexport interface FolderMutationResult {\n affectedFolders: number;\n affectedFiles: number;\n}\n\nexport interface FolderTree {\n folders: FolderRecord[];\n}\n\n/**\n * Folder lifecycle (v0.2):\n *\n * - Folders are virtual OmniCloud objects with a parent-child hierarchy.\n * - Trashing a folder trashes its whole subtree (folders + files) without\n * touching Telegram, so restore is lossless.\n * - Restoring a folder restores only the nodes that were trashed together\n * with it; anything the user trashed separately stays in the Trash.\n * - Permanent deletion removes remote objects first, then metadata.\n */\nexport class FolderService {\n constructor(\n private readonly folders: FolderRepository,\n private readonly files: FileRepository,\n private readonly engineFor: EngineResolver,\n private readonly activity: ActivityRecorder = noopActivityRecorder,\n ) {}\n\n // ── Create / read ────────────────────────────────────────────────────────\n\n async create(\n userId: string,\n input: { name: string; parentId: string | null },\n ): Promise<FolderRecord> {\n const name = sanitizeFileName(input.name);\n if (!name) throw new ValidationError(\"Invalid folder name\");\n if (input.parentId !== null) {\n const parent = await this.get(userId, input.parentId);\n if (parent.deletedAt) throw new ConflictError(\"The parent folder is in the Trash\");\n }\n const folder = await this.folders.create({ userId, parentId: input.parentId, name });\n await this.activity.record({\n userId,\n action: \"create_folder\",\n resourceType: \"folder\",\n resourceId: folder.id,\n resourceName: folder.name,\n });\n return folder;\n }\n\n /** Active folders directly inside a folder (null = root). */\n async listChildren(userId: string, parentId: string | null): Promise<FolderRecord[]> {\n if (parentId !== null) await this.get(userId, parentId);\n return this.folders.listChildren(userId, parentId);\n }\n\n async query(userId: string, input: ListQuery, page: PageRequest): Promise<Paged<FolderRecord>> {\n const query: ItemQuery = await resolveItemQuery(this.folders, userId, input);\n return this.folders.query(userId, query, page);\n }\n\n /** Every active folder for the user (used to build trees). */\n async tree(userId: string): Promise<FolderRecord[]> {\n const all = await this.folders.listByUser(userId);\n return all.filter((folder) => folder.deletedAt === null);\n }\n\n async get(userId: string, id: string): Promise<FolderRecord> {\n const record = await this.folders.findById(id);\n if (!record || record.userId !== userId) throw new NotFoundError(\"Folder not found\", \"folder\");\n return record;\n }\n\n // ── Mutations ────────────────────────────────────────────────────────────\n\n async rename(userId: string, id: string, name: string): Promise<FolderRecord> {\n const existing = await this.get(userId, id);\n const clean = sanitizeFileName(name);\n if (!clean) throw new ValidationError(\"Invalid folder name\");\n const updated = await this.folders.update(id, { name: clean });\n await this.activity.record({\n userId,\n action: \"rename\",\n resourceType: \"folder\",\n resourceId: id,\n resourceName: clean,\n metadata: { previousName: existing.name },\n });\n return updated;\n }\n\n async move(userId: string, id: string, newParentId: string | null): Promise<FolderRecord> {\n const existing = await this.get(userId, id);\n\n if (newParentId === id) throw new ValidationError(\"Cannot move a folder into itself\");\n\n if (newParentId !== null) {\n const target = await this.get(userId, newParentId);\n if (target.deletedAt) throw new ConflictError(\"The target folder is in the Trash\");\n\n // Walk up from the new parent; hitting the folder being moved means it\n // would become its own ancestor.\n let cursor: string | null = newParentId;\n while (cursor !== null) {\n if (cursor === id) {\n throw new ValidationError(\"Cannot move a folder into one of its subfolders\");\n }\n const parent = await this.folders.findById(cursor);\n if (!parent || parent.userId !== userId)\n throw new NotFoundError(\"Folder not found\", \"folder\");\n cursor = parent.parentId;\n }\n }\n\n const updated = await this.folders.update(id, { parentId: newParentId });\n await this.activity.record({\n userId,\n action: \"move\",\n resourceType: \"folder\",\n resourceId: id,\n resourceName: updated.name,\n metadata: { from: existing.parentId, to: newParentId },\n });\n return updated;\n }\n\n async setStarred(userId: string, id: string, starred: boolean): Promise<FolderRecord> {\n await this.get(userId, id);\n const updated = await this.folders.update(id, { starred });\n await this.activity.record({\n userId,\n action: starred ? \"star\" : \"unstar\",\n resourceType: \"folder\",\n resourceId: id,\n resourceName: updated.name,\n });\n return updated;\n }\n\n // ── Trash lifecycle ──────────────────────────────────────────────────────\n\n /**\n * Moves a folder and its entire subtree to the Trash. Nothing is deleted\n * from Telegram, so restore is lossless.\n */\n async trash(userId: string, id: string): Promise<FolderMutationResult> {\n const record = await this.get(userId, id);\n if (record.deletedAt) {\n return { affectedFolders: 1, affectedFiles: 0 };\n }\n\n const subtree = await this.collectSubtree(userId, id);\n const fileIds = await this.collectFileIds(userId, subtree);\n\n // One batch id stamps everything this operation trashes, so restore can\n // bring back exactly this set without relying on timestamp equality.\n const batchId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n const stampedAt = new Date();\n await this.folders.updateMany(\n subtree.map((folder) => folder.id),\n { deletedAt: stampedAt, trashBatchId: batchId },\n );\n await this.files.updateMany(fileIds, { deletedAt: stampedAt, trashBatchId: batchId });\n\n await this.activity.record({\n userId,\n action: \"trash\",\n resourceType: \"folder\",\n resourceId: id,\n resourceName: record.name,\n metadata: { folders: subtree.length, files: fileIds.length },\n });\n\n return { affectedFolders: subtree.length, affectedFiles: fileIds.length };\n }\n\n /**\n * Restores a folder subtree. Only nodes trashed at the same instant as the\n * folder itself are restored, so items the user trashed separately stay put.\n */\n async restore(userId: string, id: string): Promise<FolderMutationResult> {\n const record = await this.get(userId, id);\n\n // If the parent chain is still trashed, restore to the root instead of\n // creating a folder the user cannot see.\n let parentId = record.parentId;\n if (parentId !== null) {\n const parent = await this.folders.findById(parentId);\n if (!parent || parent.deletedAt) parentId = null;\n }\n\n const subtree = await this.collectSubtree(userId, id, { includeTrashed: true });\n const batchId = record.trashBatchId;\n\n // Prefer the batch marker; fall back to timestamp equality for rows\n // created before the marker existed (v0.2 upgrade path).\n const belongsToBatch = (deletedAt: Date | null, marker: string | null): boolean =>\n deletedAt !== null &&\n (batchId !== null\n ? marker === batchId\n : record.deletedAt !== null && deletedAt.getTime() === record.deletedAt.getTime());\n\n const restorableFolders = subtree.filter((folder) =>\n belongsToBatch(folder.deletedAt, folder.trashBatchId),\n );\n const restorableFolderIds = new Set(restorableFolders.map((folder) => folder.id));\n\n const allFiles = await this.files.listByUser(userId);\n const restorableFiles = allFiles.filter(\n (file) =>\n file.folderId !== null &&\n restorableFolderIds.has(file.folderId) &&\n belongsToBatch(file.deletedAt, file.trashBatchId),\n );\n\n const restorableFileIds = restorableFiles.map((file) => file.id);\n if (restorableFolders.length > 0) {\n await this.folders.updateMany(\n restorableFolders.map((folder) => folder.id),\n { deletedAt: null, trashBatchId: null },\n );\n await this.folders.update(id, { parentId });\n }\n if (restorableFileIds.length > 0) {\n await this.files.updateMany(restorableFileIds, { deletedAt: null, trashBatchId: null });\n }\n\n await this.activity.record({\n userId,\n action: \"restore\",\n resourceType: \"folder\",\n resourceId: id,\n resourceName: record.name,\n metadata: { folders: restorableFolders.length, files: restorableFiles.length },\n });\n\n return { affectedFolders: restorableFolders.length, affectedFiles: restorableFiles.length };\n }\n\n /**\n * Permanently deletes a folder subtree: remote objects first, then\n * metadata. Files whose remote delete fails are left in the Trash so the\n * user can retry rather than losing data silently.\n */\n async deletePermanently(userId: string, id: string): Promise<FolderMutationResult> {\n await this.get(userId, id);\n\n const subtree = await this.collectSubtree(userId, id, { includeTrashed: true });\n const folderIds = subtree.map((folder) => folder.id);\n\n const allFiles = await this.files.listByUser(userId);\n const affected = allFiles.filter(\n (file) => file.folderId !== null && folderIds.includes(file.folderId),\n );\n\n const engine = await this.engineFor(userId);\n const removedFileIds: string[] = [];\n for (const file of affected) {\n try {\n await engine.remove({ messageId: String(file.telegramMessageId) });\n removedFileIds.push(file.id);\n } catch {\n // Keep the metadata so the operation can be retried.\n }\n }\n\n if (removedFileIds.length > 0) {\n await this.files.deleteVersionsByFileIds(removedFileIds);\n await this.files.deleteMany(removedFileIds);\n }\n\n // Only delete folders whose files are all gone, plus empty leaves.\n const remainingFolderIds = new Set(\n affected.filter((file) => !removedFileIds.includes(file.id)).map((file) => file.folderId),\n );\n const deletable = folderIds.filter((folderId) => !remainingFolderIds.has(folderId));\n if (deletable.length > 0) {\n await this.folders.deleteMany(deletable);\n }\n\n await this.activity.record({\n userId,\n action: \"delete\",\n resourceType: \"folder\",\n resourceId: id,\n resourceName: subtree[0]?.name ?? \"folder\",\n metadata: { folders: deletable.length, files: removedFileIds.length },\n });\n\n return { affectedFolders: deletable.length, affectedFiles: removedFileIds.length };\n }\n\n /**\n * v0.1-compatible recursive delete. Now routes through the v0.2 permanent\n * delete so remote cleanup and orphan prevention are consistent.\n */\n async delete(userId: string, id: string): Promise<FolderMutationResult> {\n return this.deletePermanently(userId, id);\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n /** The folder plus every descendant (BFS), optionally including trashed. */\n private async collectSubtree(\n userId: string,\n rootId: string,\n options: { includeTrashed?: boolean } = {},\n ): Promise<FolderRecord[]> {\n const all = await this.folders.listByUser(userId);\n const childrenOf = new Map<string | null, FolderRecord[]>();\n for (const folder of all) {\n const list = childrenOf.get(folder.parentId) ?? [];\n list.push(folder);\n childrenOf.set(folder.parentId, list);\n }\n\n const root = all.find((folder) => folder.id === rootId);\n if (!root) throw new NotFoundError(\"Folder not found\", \"folder\");\n\n const collected: FolderRecord[] = [root];\n const queue: string[] = [rootId];\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const child of childrenOf.get(current) ?? []) {\n if (!options.includeTrashed && child.deletedAt) continue;\n collected.push(child);\n queue.push(child.id);\n }\n }\n return collected;\n }\n\n /** Ids of files inside the given folders that are not already trashed. */\n private async collectFileIds(userId: string, folders: FolderRecord[]): Promise<string[]> {\n const ids = new Set(folders.map((folder) => folder.id));\n const allFiles = await this.files.listByUser(userId);\n return allFiles\n .filter((file) => file.deletedAt === null && file.folderId !== null && ids.has(file.folderId))\n .map((file) => file.id);\n }\n}\n","import { paginationMeta, parseSearchQuery } from \"@omnicloud/shared\";\nimport type { ListQuery, PaginationDTO, SearchResultDTO } from \"@omnicloud/shared\";\nimport { ValidationError } from \"../errors\";\nimport type { FileRecord, FolderRecord, PageRequest } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\nimport { resolveItemQuery } from \"./query-resolver\";\n\nconst MAX_QUERY_LENGTH = 200;\n\nexport interface SearchResult {\n files: FileRecord[];\n folders: FolderRecord[];\n pagination: PaginationDTO;\n}\n\n/**\n * PostgreSQL-backed search (no Elasticsearch, per the v0.2 scope).\n *\n * The user query is parsed into structured filters (`type:`, `size:`,\n * `folder:`, …) and evaluated by the repository layer, so results respect\n * ownership and stay safely parameterized.\n */\nexport class SearchService {\n constructor(\n private readonly files: FileRepository,\n private readonly folders: FolderRepository,\n ) {}\n\n async search(\n userId: string,\n rawQuery: string,\n page: PageRequest,\n overrides: ListQuery = {},\n ): Promise<SearchResult> {\n const trimmed = rawQuery.trim();\n if (!trimmed) throw new ValidationError(\"Search query must not be empty\");\n if (trimmed.length > MAX_QUERY_LENGTH) {\n throw new ValidationError(`Search query must be at most ${MAX_QUERY_LENGTH} characters`);\n }\n\n const { query } = parseSearchQuery(trimmed, overrides);\n // Search covers both active and trashed items unless the caller narrows it.\n if (query.status === undefined) query.status = \"all\";\n\n const itemQuery = await resolveItemQuery(this.folders, userId, query);\n\n const [filePage, folderPage] = await Promise.all([\n this.files.query(userId, itemQuery, page),\n this.folders.query(userId, itemQuery, page),\n ]);\n\n // The pagination envelope describes the combined result count so the UI\n // can render a single \"load more\" affordance.\n const total = filePage.total + folderPage.total;\n return {\n files: filePage.items,\n folders: folderPage.items,\n pagination: paginationMeta(page.page, page.limit, total),\n };\n }\n\n /** Convenience wrapper returning the shared DTO shape. */\n async searchDTO(\n userId: string,\n rawQuery: string,\n page: PageRequest,\n ): Promise<Omit<SearchResultDTO, \"files\" | \"folders\"> & SearchResult> {\n return this.search(userId, rawQuery, page);\n }\n}\n","import { fileCategory } from \"@omnicloud/shared\";\nimport type { StorageStats } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\n\nconst LARGEST_FILES = 10;\n\n/**\n * Storage dashboard statistics, computed from PostgreSQL metadata only —\n * no Telegram round-trips, so the page is fast and works offline.\n */\nexport class StatsService {\n constructor(\n private readonly files: FileRepository,\n private readonly folders: FolderRepository,\n ) {}\n\n async stats(userId: string, quotaBytes: number | null = null): Promise<StorageStats> {\n const [counters, allFiles] = await Promise.all([\n this.files.statsByUser(userId),\n // Metadata-only aggregation; v0.2 datasets fit comfortably in memory.\n this.files.listByUser(userId),\n ]);\n\n const active = allFiles.filter((file) => file.deletedAt === null);\n const categoryBytes = new Map<string, { bytes: number; count: number }>();\n\n for (const file of active) {\n const category = fileCategory(file.mimeType);\n const bucket = categoryBytes.get(category) ?? { bytes: 0, count: 0 };\n bucket.bytes += file.size;\n bucket.count += 1;\n categoryBytes.set(category, bucket);\n }\n\n const byType = [...categoryBytes.entries()]\n .map(([category, bucket]) => ({ category, bytes: bucket.bytes, count: bucket.count }))\n .sort((a, b) => b.bytes - a.bytes);\n\n const largestFiles = [...active].sort((a, b) => b.size - a.size).slice(0, LARGEST_FILES);\n\n return {\n fileCount: counters.fileCount,\n folderCount: counters.folderCount,\n totalBytes: counters.totalBytes,\n trashBytes: counters.trashBytes,\n trashFileCount: counters.trashFileCount,\n starredCount: counters.starredCount,\n byType,\n largestFiles,\n quotaBytes,\n };\n }\n}\n","import { paginationMeta } from \"@omnicloud/shared\";\nimport type { ListQuery, PaginationDTO } from \"@omnicloud/shared\";\nimport type { FileRecord, FolderRecord, PageRequest } from \"../types\";\nimport type { FileRepository, FolderRepository } from \"../repos\";\nimport { NotFoundError } from \"../errors\";\nimport type { EngineResolver } from \"./file-service\";\nimport { resolveItemQuery } from \"./query-resolver\";\n\nexport interface TrashListing {\n files: FileRecord[];\n folders: FolderRecord[];\n pagination: PaginationDTO;\n}\n\nexport interface EmptyTrashResult {\n deletedFiles: number;\n deletedFolders: number;\n failedFiles: number;\n}\n\n/**\n * Trash is a virtual view over soft-deleted rows. Listing shows only the\n * explicitly trashed items; emptying removes their remote objects before the\n * metadata, and reports failures instead of hiding them.\n */\nexport class TrashService {\n constructor(\n private readonly files: FileRepository,\n private readonly folders: FolderRepository,\n private readonly engineFor: EngineResolver,\n ) {}\n\n async list(userId: string, page: PageRequest, extra: ListQuery = {}): Promise<TrashListing> {\n const itemQuery = await resolveItemQuery(this.folders, userId, {\n ...extra,\n status: \"trashed\",\n folderId: undefined,\n });\n\n const [filePage, folderPage] = await Promise.all([\n this.files.query(userId, itemQuery, page),\n this.folders.query(userId, itemQuery, page),\n ]);\n\n const total = filePage.total + folderPage.total;\n return {\n files: filePage.items,\n folders: folderPage.items,\n pagination: paginationMeta(page.page, page.limit, total),\n };\n }\n\n /**\n * Permanently removes everything in the Trash for a user. Folders are\n * processed root-first so removing a parent also removes its contents.\n */\n async empty(userId: string): Promise<EmptyTrashResult> {\n const engine = await this.engineFor(userId);\n const allFiles = await this.files.listByUser(userId);\n const trashedFiles = allFiles.filter((file) => file.deletedAt !== null);\n\n let deletedFiles = 0;\n let failedFiles = 0;\n const deletedFileIds: string[] = [];\n\n for (const file of trashedFiles) {\n try {\n await engine.remove({ messageId: String(file.telegramMessageId) });\n deletedFileIds.push(file.id);\n deletedFiles += 1;\n } catch {\n failedFiles += 1;\n }\n }\n\n if (deletedFileIds.length > 0) {\n await this.files.deleteVersionsByFileIds(deletedFileIds);\n await this.files.deleteMany(deletedFileIds);\n }\n\n // Remove trashed folders, shallowest first, and only those with no\n // remaining files inside.\n const allFolders = await this.folders.listByUser(userId);\n const trashedFolders = allFolders.filter((folder) => folder.deletedAt !== null);\n const depth = (folder: FolderRecord): number => {\n let level = 0;\n let cursor = folder.parentId;\n const seen = new Set<string>();\n while (cursor !== null && !seen.has(cursor)) {\n seen.add(cursor);\n const parent = allFolders.find((candidate) => candidate.id === cursor);\n if (!parent) break;\n level += 1;\n cursor = parent.parentId;\n }\n return level;\n };\n trashedFolders.sort((a, b) => depth(a) - depth(b));\n\n let deletedFolders = 0;\n for (const folder of trashedFolders) {\n // Skip folders that still contain files we could not delete.\n const stillHasFiles = await this.files.query(\n userId,\n { folderId: folder.id, trashed: true },\n { page: 1, limit: 1 },\n );\n if (stillHasFiles.total > 0) continue;\n await this.folders.deleteMany([folder.id]);\n deletedFolders += 1;\n }\n\n return { deletedFiles, deletedFolders, failedFiles };\n }\n\n /** Restores a single trashed item (used by the batch restore endpoint). */\n async verifyTrashedFile(userId: string, id: string): Promise<FileRecord> {\n const file = await this.files.findById(id);\n if (!file || file.userId !== userId || file.deletedAt === null) {\n throw new NotFoundError(\"File is not in the Trash\", \"file\");\n }\n return file;\n }\n}\n","import type { IntegrityIssueDTO, IntegrityReportDTO } from \"@omnicloud/shared\";\nimport type { FileRecord } from \"../types\";\nimport type { FileRepository } from \"../repos\";\nimport type { EngineResolver } from \"./file-service\";\n\nexport interface IntegrityCheckOptions {\n /** Re-download and re-hash objects (slow but authoritative). */\n deep?: boolean;\n /** Maximum files to inspect in one pass. */\n limit?: number;\n}\n\n/**\n * Detects drift between PostgreSQL metadata and Telegram storage:\n *\n * - `missing` — metadata exists, the Telegram message is gone\n * - `size_mismatch` — the stored object's size differs from the metadata\n * - `hash_mismatch` — deep mode only: downloaded bytes don't match sha256\n * - `unreadable` — the provider errored while inspecting the object\n *\n * The checker is strictly read-only: repairs are a separate, explicit step so\n * nothing is ever deleted silently (see the v0.2 reliability rules).\n */\nexport class IntegrityService {\n constructor(\n private readonly files: FileRepository,\n private readonly engineFor: EngineResolver,\n ) {}\n\n async check(userId: string, options: IntegrityCheckOptions = {}): Promise<IntegrityReportDTO> {\n const startedAt = Date.now();\n const all = await this.files.listByUser(userId);\n const active = all.filter((file) => file.deletedAt === null);\n const targets = options.limit ? active.slice(0, options.limit) : active;\n\n const engine = await this.engineFor(userId);\n const issues: IntegrityIssueDTO[] = [];\n let healthy = 0;\n\n for (const file of targets) {\n const issue = await this.inspect(file, options.deep ?? false, engine);\n if (issue) issues.push(issue);\n else healthy += 1;\n }\n\n const missing = issues.filter((issue) => issue.kind === \"missing\").length;\n const inconsistent = issues.filter(\n (issue) => issue.kind === \"size_mismatch\" || issue.kind === \"hash_mismatch\",\n ).length;\n const unreadable = issues.filter((issue) => issue.kind === \"unreadable\").length;\n\n return {\n checkedAt: new Date().toISOString(),\n filesChecked: targets.length,\n healthy,\n missing,\n inconsistent,\n unreadable,\n issues,\n durationMs: Date.now() - startedAt,\n };\n }\n\n private async inspect(\n file: FileRecord,\n deep: boolean,\n engine: Awaited<ReturnType<EngineResolver>>,\n ): Promise<IntegrityIssueDTO | null> {\n const ref = { messageId: String(file.telegramMessageId) };\n\n let stat: Awaited<ReturnType<typeof engine.stat>>;\n try {\n stat = await engine.stat(ref);\n } catch (error) {\n return {\n fileId: file.id,\n name: file.name,\n kind: \"unreadable\",\n detail: error instanceof Error ? error.message : \"Storage lookup failed\",\n };\n }\n\n if (!stat) {\n return {\n fileId: file.id,\n name: file.name,\n kind: \"missing\",\n detail: \"No message with this id exists in the storage channel\",\n };\n }\n\n if (stat.size !== file.size) {\n return {\n fileId: file.id,\n name: file.name,\n kind: \"size_mismatch\",\n detail: `metadata=${file.size} storage=${stat.size}`,\n };\n }\n\n if (deep) {\n try {\n const data = await engine.download(ref);\n const ok = await engine.verifyIntegrity(data, file.sha256);\n if (!ok) {\n return {\n fileId: file.id,\n name: file.name,\n kind: \"hash_mismatch\",\n detail: \"Downloaded bytes do not match the stored SHA-256\",\n };\n }\n } catch (error) {\n return {\n fileId: file.id,\n name: file.name,\n kind: \"unreadable\",\n detail: error instanceof Error ? error.message : \"Download failed\",\n };\n }\n }\n\n return null;\n }\n}\n","import { paginationMeta } from \"@omnicloud/shared\";\nimport type { ActivityAction, RecentItemDTO, RecentPageDTO } from \"@omnicloud/shared\";\nimport type { PageRequest } from \"../types\";\nimport type { ActivityRepository, FileRepository } from \"../repos\";\n\n/** Actions that count as meaningful \"recent\" activity for a file. */\nconst RECENT_ACTIONS: ActivityAction[] = [\n \"upload\",\n \"download\",\n \"open\",\n \"rename\",\n \"move\",\n \"replace\",\n];\n\n/**\n * Recent view: the most recently touched active files, newest activity first.\n * Trashed and permanently-deleted files are filtered out so the view never\n * surfaces items the user cannot open.\n */\nexport class RecentService {\n constructor(\n private readonly activity: ActivityRepository,\n private readonly files: FileRepository,\n ) {}\n\n async list(userId: string, page: PageRequest): Promise<RecentPageDTO> {\n // Pull a bounded window of recent *meaningful* events (the repository\n // dedups by file), then resolve the distinct files.\n const window = Math.min(page.page * page.limit * 3, 500);\n const events = await this.activity.recentFiles(userId, RECENT_ACTIONS, window);\n\n const seen = new Set<string>();\n for (const event of events) {\n seen.add(event.resourceId);\n }\n\n const fileIds = [...seen];\n const records = fileIds.length > 0 ? await this.files.listByIds(userId, fileIds) : [];\n const byId = new Map(records.map((record) => [record.id, record]));\n\n const items: RecentItemDTO[] = [];\n for (const fileId of fileIds) {\n const record = byId.get(fileId);\n if (!record || record.deletedAt !== null) continue;\n const event = events.find((candidate) => candidate.resourceId === fileId)!;\n items.push({\n file: {\n id: record.id,\n name: record.name,\n size: record.size,\n mimeType: record.mimeType,\n sha256: record.sha256,\n folderId: record.folderId,\n starred: record.starred,\n trashed: false,\n deletedAt: null,\n versionCount: record.versionCount,\n currentVersionId: record.currentVersionId,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n },\n lastAction: event.action,\n lastActionAt: event.createdAt.toISOString(),\n });\n }\n\n const start = (page.page - 1) * page.limit;\n const pageItems = items.slice(start, start + page.limit);\n\n return {\n items: pageItems,\n pagination: paginationMeta(page.page, page.limit, items.length),\n };\n }\n}\n","import { Api, TelegramClient } from \"telegram\";\nimport { StringSession } from \"telegram/sessions/index.js\";\nimport {\n StorageUnavailableError,\n TelegramAuthRequiredError,\n TelegramConnectionError,\n UnauthorizedError,\n} from \"@omnicloud/core\";\nimport type { ConnectionState, SessionRepository, StorageHealth } from \"@omnicloud/core\";\nimport { mapTelegramError } from \"./errors\";\n\nexport interface TelegramCredentials {\n apiId: number;\n apiHash: string;\n}\n\ninterface ManagedConnection {\n client: TelegramClient;\n state: ConnectionState;\n /** In-flight connect/reconnect promise, so callers can await it. */\n pending: Promise<TelegramClient> | null;\n lastError: string | null;\n lastCheckAt: number | null;\n lastCheckOk: boolean | null;\n}\n\nexport interface ConnectionStatus {\n state: ConnectionState;\n lastError: string | null;\n lastCheckAt: Date | null;\n healthy: boolean | null;\n}\n\n/** How long a health probe is cached before re-probing. */\nconst HEALTH_TTL_MS = 30_000;\n/** Minimum gap between reconnect attempts for the same user. */\nconst RECONNECT_COOLDOWN_MS = 5_000;\n\n/**\n * Owns one Telegram (MTProto) connection per authenticated user and tracks an\n * explicit lifecycle state for each:\n *\n * DISCONNECTED → CONNECTING → CONNECTED\n * ↘ RECONNECTING → CONNECTED\n * ↘ ERROR | AUTH_REQUIRED\n *\n * Responsibilities: connect, disconnect, reconnect, validateSession,\n * healthCheck, refreshConnection. Sessions never leave the server.\n */\nexport class TelegramClientManager {\n private connections = new Map<string, ManagedConnection>();\n private lastReconnectAt = new Map<string, number>();\n\n constructor(\n private readonly credentials: TelegramCredentials,\n private readonly sessions: SessionRepository,\n ) {}\n\n /** Returns a connected client, (re)connecting when needed. */\n async getForUser(userId: string): Promise<TelegramClient> {\n const existing = this.connections.get(userId);\n\n if (existing) {\n if (existing.state === \"CONNECTED\" && this.isConnected(existing.client)) {\n return existing.client;\n }\n if (existing.pending) return existing.pending;\n if (existing.state === \"AUTH_REQUIRED\") {\n throw new TelegramAuthRequiredError();\n }\n return this.reconnect(userId, existing);\n }\n\n return this.connectUser(userId);\n }\n\n /** Connection status for health reporting and the storage dashboard. */\n statusForUser(userId: string): ConnectionStatus {\n const connection = this.connections.get(userId);\n if (!connection) {\n return { state: \"DISCONNECTED\", lastError: null, lastCheckAt: null, healthy: null };\n }\n return {\n state: connection.state,\n lastError: connection.lastError,\n lastCheckAt: connection.lastCheckAt ? new Date(connection.lastCheckAt) : null,\n healthy: connection.lastCheckOk,\n };\n }\n\n /**\n * Probes a user's connection. Cheap by default (connection state only);\n * `deep` performs a real Telegram round-trip to validate the session.\n */\n async healthCheck(userId: string, deep = false): Promise<StorageHealth> {\n const connection = this.connections.get(userId);\n\n if (!connection) {\n try {\n await this.getForUser(userId);\n } catch (error) {\n const state: ConnectionState =\n error instanceof TelegramAuthRequiredError ? \"AUTH_REQUIRED\" : \"ERROR\";\n const current = this.connections.get(userId);\n if (current) current.state = state;\n return {\n healthy: false,\n latencyMs: null,\n message: error instanceof Error ? error.message : \"Telegram is not connected\",\n targetTitle: null,\n };\n }\n }\n\n const active = this.connections.get(userId)!;\n\n if (!deep && active.lastCheckAt && Date.now() - active.lastCheckAt < HEALTH_TTL_MS) {\n return {\n healthy: active.lastCheckOk ?? false,\n latencyMs: null,\n message: active.lastError,\n targetTitle: null,\n };\n }\n\n const startedAt = Date.now();\n try {\n await active.client.getMe();\n active.lastCheckAt = Date.now();\n active.lastCheckOk = true;\n active.lastError = null;\n active.state = \"CONNECTED\";\n return {\n healthy: true,\n latencyMs: Date.now() - startedAt,\n message: null,\n targetTitle: null,\n };\n } catch (error) {\n const mapped = mapTelegramError(error, \"Telegram health check failed\");\n active.lastCheckAt = Date.now();\n active.lastCheckOk = false;\n active.lastError = mapped.message;\n active.state =\n mapped instanceof TelegramAuthRequiredError || mapped instanceof UnauthorizedError\n ? \"AUTH_REQUIRED\"\n : \"ERROR\";\n return {\n healthy: false,\n latencyMs: Date.now() - startedAt,\n message: mapped.message,\n targetTitle: null,\n };\n }\n }\n\n /** Registers an already-authenticated client (from the login flow). */\n cacheClient(userId: string, client: TelegramClient): void {\n this.connections.set(userId, {\n client,\n state: \"CONNECTED\",\n pending: null,\n lastError: null,\n lastCheckAt: Date.now(),\n lastCheckOk: true,\n });\n }\n\n /** Drops a user's connection (e.g. after logout) without touching storage. */\n async disconnectUser(userId: string): Promise<void> {\n const connection = this.connections.get(userId);\n if (!connection) return;\n this.connections.delete(userId);\n try {\n await connection.client.disconnect();\n } catch {\n // best effort\n }\n }\n\n async disconnectAll(): Promise<void> {\n const ids = [...this.connections.keys()];\n await Promise.all(ids.map((userId) => this.disconnectUser(userId)));\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private isConnected(client: TelegramClient): boolean {\n try {\n return client.connected === true;\n } catch {\n return false;\n }\n }\n\n private async connectUser(userId: string): Promise<TelegramClient> {\n const session = await this.sessions.get(userId);\n if (!session) {\n throw new TelegramAuthRequiredError(\"Telegram account is not connected\");\n }\n\n const connection: ManagedConnection = {\n client: null as unknown as TelegramClient,\n state: \"CONNECTING\",\n pending: null,\n lastError: null,\n lastCheckAt: null,\n lastCheckOk: null,\n };\n\n const attempt = this.establish(session.stringSession).then((client) => {\n connection.client = client;\n connection.state = \"CONNECTED\";\n connection.pending = null;\n connection.lastError = null;\n return client;\n });\n connection.pending = attempt;\n this.connections.set(userId, connection);\n\n try {\n return await attempt;\n } catch (error) {\n connection.pending = null;\n const mapped = mapTelegramError(error, \"Could not connect to Telegram\");\n connection.state =\n mapped instanceof TelegramAuthRequiredError || mapped instanceof UnauthorizedError\n ? \"AUTH_REQUIRED\"\n : \"ERROR\";\n connection.lastError = mapped.message;\n throw mapped;\n }\n }\n\n private async reconnect(userId: string, connection: ManagedConnection): Promise<TelegramClient> {\n const lastAttempt = this.lastReconnectAt.get(userId) ?? 0;\n if (Date.now() - lastAttempt < RECONNECT_COOLDOWN_MS && connection.pending) {\n return connection.pending;\n }\n this.lastReconnectAt.set(userId, Date.now());\n\n connection.state = \"RECONNECTING\";\n const session = await this.sessions.get(userId);\n if (!session) {\n connection.state = \"AUTH_REQUIRED\";\n throw new TelegramAuthRequiredError(\"Telegram account is not connected\");\n }\n\n const attempt = (async () => {\n try {\n await connection.client.disconnect().catch(() => undefined);\n } catch {\n // ignore\n }\n return this.establish(session.stringSession);\n })()\n .then((client) => {\n connection.client = client;\n connection.state = \"CONNECTED\";\n connection.pending = null;\n connection.lastError = null;\n return client;\n })\n .catch((error: unknown) => {\n connection.pending = null;\n const mapped = mapTelegramError(error, \"Could not reconnect to Telegram\");\n connection.state =\n mapped instanceof TelegramAuthRequiredError || mapped instanceof UnauthorizedError\n ? \"AUTH_REQUIRED\"\n : \"ERROR\";\n connection.lastError = mapped.message;\n throw mapped;\n });\n\n connection.pending = attempt;\n return attempt;\n }\n\n private async establish(sessionString: string): Promise<TelegramClient> {\n const client = new TelegramClient(\n new StringSession(sessionString),\n this.credentials.apiId,\n this.credentials.apiHash,\n { connectionRetries: 3 },\n );\n try {\n await client.connect();\n // Validate the session immediately so expired sessions surface as\n // AUTH_REQUIRED instead of a generic failure on the first operation.\n if (!client.connected) {\n throw new TelegramConnectionError(\"Telegram client did not connect\");\n }\n return client;\n } catch (error) {\n throw mapTelegramError(error, \"Could not connect to Telegram\");\n }\n }\n}\n\nexport function newTelegramClient(credentials: TelegramCredentials): TelegramClient {\n return new TelegramClient(new StringSession(\"\"), credentials.apiId, credentials.apiHash, {\n connectionRetries: 3,\n });\n}\n\n/**\n * Extracts the freshly created broadcast channel from a\n * channels.CreateChannel response.\n */\nexport function extractCreatedChannel(result: Api.TypeUpdates): Api.Channel {\n const chats =\n result instanceof Api.Updates || result instanceof Api.UpdatesCombined ? result.chats : [];\n for (const chat of chats) {\n if (chat instanceof Api.Channel) return chat;\n }\n throw new TelegramConnectionError(\"Telegram did not return the created storage channel\");\n}\n\nexport { StorageUnavailableError };\n","import { RPCError } from \"telegram/errors/index.js\";\nimport {\n DomainError,\n ForbiddenError,\n RateLimitedError,\n StorageNotInitializedError,\n TelegramAuthRequiredError,\n TelegramConnectionError,\n TelegramFileNotFoundError,\n UnauthorizedError,\n ValidationError,\n} from \"@omnicloud/core\";\n\nexport function isTelegramRpcError(error: unknown): error is RPCError {\n return error instanceof RPCError;\n}\n\n/** True when the error signals that 2FA password auth is required. */\nexport function isPasswordRequiredError(error: unknown): boolean {\n return isTelegramRpcError(error) && error.errorMessage === \"SESSION_PASSWORD_NEEDED\";\n}\n\n/**\n * Maps a Telegram (GramJS) failure to a structured domain error with an\n * appropriate HTTP status and a client-facing message that never leaks\n * credentials or session material.\n */\nexport function mapTelegramError(error: unknown, fallbackMessage: string): DomainError {\n if (error instanceof DomainError) return error;\n\n if (isTelegramRpcError(error)) {\n const msg = error.errorMessage ?? \"\";\n\n if (msg.startsWith(\"FLOOD_WAIT_\")) {\n const seconds = Number(msg.slice(\"FLOOD_WAIT_\".length)) || 60;\n return new RateLimitedError(`Telegram rate limit reached — retry in ${seconds} seconds`);\n }\n\n switch (msg) {\n case \"PHONE_NUMBER_INVALID\":\n return new ValidationError(\"This phone number is not valid\");\n case \"PHONE_NUMBER_UNOCCUPIED\":\n return new ValidationError(\"No Telegram account exists for this phone number\");\n case \"PHONE_CODE_INVALID\":\n return new ValidationError(\"Invalid confirmation code\");\n case \"PHONE_CODE_EXPIRED\":\n return new ValidationError(\"The confirmation code has expired — request a new one\");\n case \"PHONE_CODE_EMPTY\":\n return new ValidationError(\"Please enter the confirmation code\");\n case \"SESSION_PASSWORD_NEEDED\":\n return new UnauthorizedError(\"Two-factor authentication is enabled on this account\");\n case \"PASSWORD_HASH_INVALID\":\n return new ValidationError(\"Incorrect two-factor password\");\n case \"PHONE_NUMBER_BANNED\":\n return new ForbiddenError(\"This phone number is banned from Telegram\");\n case \"AUTH_KEY_UNREGISTERED\":\n case \"AUTH_KEY_DUPLICATED\":\n case \"SESSION_REVOKED\":\n case \"SESSION_EXPIRED\":\n case \"USER_DEACTIVATED\":\n case \"USER_DEACTIVATED_BAN\":\n return new TelegramAuthRequiredError();\n case \"CHANNEL_PRIVATE\":\n case \"CHANNEL_INVALID\":\n case \"CHAT_ID_INVALID\":\n return new StorageNotInitializedError();\n case \"MSG_ID_INVALID\":\n case \"MESSAGE_ID_INVALID\":\n return new TelegramFileNotFoundError();\n case \"FILE_REFERENCE_EXPIRED\":\n case \"FILE_REFERENCE_INVALID\":\n return new TelegramFileNotFoundError(\n \"The Telegram file reference expired — re-upload the file to refresh it\",\n );\n default:\n return new TelegramConnectionError(fallbackMessage, {\n code: error.code,\n telegramError: msg,\n });\n }\n }\n\n const detail = error instanceof Error ? error.message : String(error);\n // Network-shaped failures get the connection error code so the UI can offer\n // a retry rather than implying the request was invalid.\n if (/timeout|ECONNRESET|ENOTFOUND|socket|network|connect/i.test(detail)) {\n return new TelegramConnectionError(`${fallbackMessage} (network error)`, error);\n }\n return new TelegramConnectionError(\n detail ? `${fallbackMessage}: ${detail}` : fallbackMessage,\n error,\n );\n}\n","import { Readable } from \"node:stream\";\nimport { Api, TelegramClient } from \"telegram\";\nimport bigInt from \"big-integer\";\nimport { CustomFile } from \"telegram/client/uploads.js\";\nimport {\n OperationCancelledError,\n TelegramAuthRequiredError,\n TelegramConnectionError,\n TelegramFileNotFoundError,\n UploadFailedError,\n type StorageHealth,\n type StorageProvider,\n type StorageUploadInput,\n type StoredObject,\n type StoredRef,\n type TransferControl,\n} from \"@omnicloud/core\";\nimport { mapTelegramError } from \"./errors\";\n\nexport interface TelegramStorageOptions {\n /** Raw channel id of the user's private storage channel. */\n chatId: string;\n /** Channel access hash required to build the input peer. */\n accessHash: string;\n /** Human-readable channel title, surfaced in health reporting. */\n title?: string;\n}\n\n/** Telegram's own limits for a single uploaded document. */\nconst MAX_DOCUMENT_BYTES = 2 * 1024 * 1024 * 1024; // 2 GB\n\n/**\n * StorageProvider implementation backed by the user's private Telegram\n * channel. Files are sent as force-downloaded documents (no compression), so\n * every upload is a document message in the channel.\n *\n * v0.2 adds progress reporting, cancellation, streamed downloads and a\n * health check, all translated into the provider-agnostic core vocabulary.\n */\nexport class TelegramStorageProvider implements StorageProvider {\n readonly name = \"telegram\";\n private readonly peer: Api.InputPeerChannel;\n private readonly title: string | null;\n\n constructor(\n private readonly client: TelegramClient,\n options: TelegramStorageOptions,\n ) {\n this.peer = new Api.InputPeerChannel({\n channelId: bigInt(options.chatId),\n accessHash: bigInt(options.accessHash),\n });\n this.title = options.title ?? null;\n }\n\n // ── Upload ───────────────────────────────────────────────────────────────\n\n async put(input: StorageUploadInput, control: TransferControl = {}): Promise<StoredObject> {\n const data = await this.materialize(input);\n if (data.byteLength > MAX_DOCUMENT_BYTES) {\n throw new UploadFailedError(\n `Telegram does not accept documents larger than ${MAX_DOCUMENT_BYTES / (1024 * 1024 * 1024)} GB`,\n );\n }\n this.throwIfAborted(control.signal);\n\n const total = data.byteLength;\n control.onProgress?.({ transferred: 0, total, percent: 0 });\n\n try {\n const file = new CustomFile(input.name, total, \"\", data);\n const message = await this.client.sendFile(this.peer, {\n file,\n forceDocument: true,\n workers: 1,\n // Surface transport-level progress to the caller. GramJS reports a\n // 0..1 fraction. `isCanceled` lets us abort an in-flight upload.\n progressCallback: Object.assign(\n (fraction: number) => {\n const clamped = Math.max(0, Math.min(1, fraction));\n control.onProgress?.({\n transferred: Math.round(clamped * total),\n total,\n percent: Math.round(clamped * 100),\n });\n },\n {\n get isCanceled(): boolean {\n return control.signal?.aborted === true;\n },\n },\n ),\n });\n\n if (!(message instanceof Api.Message)) {\n throw new UploadFailedError(\"Telegram returned an unexpected upload response\");\n }\n\n control.onProgress?.({ transferred: total, total, percent: 100 });\n return {\n messageId: message.id.toString(),\n name: input.name,\n size: total,\n mimeType: input.mimeType,\n };\n } catch (error) {\n if (error instanceof OperationCancelledError) throw error;\n throw mapTelegramError(error, \"Telegram upload failed\");\n }\n }\n\n // ── Download ─────────────────────────────────────────────────────────────\n\n async get(ref: StoredRef, control: TransferControl = {}): Promise<Buffer> {\n const message = await this.requireMessage(ref);\n this.throwIfAborted(control.signal);\n\n const size = this.documentSize(message);\n control.onProgress?.({ transferred: 0, total: size, percent: 0 });\n\n let data: Buffer | string | undefined;\n try {\n data = await this.client.downloadMedia(message, {\n progressCallback: (downloaded: bigInt.BigInteger, total: bigInt.BigInteger) => {\n const transferred = Number(downloaded.toString());\n const totalBytes = Number(total.toString());\n control.onProgress?.({\n transferred,\n total: totalBytes,\n percent: totalBytes > 0 ? Math.round((transferred / totalBytes) * 100) : null,\n });\n },\n });\n } catch (error) {\n throw mapTelegramError(error, \"Telegram download failed\");\n }\n\n if (!data || typeof data === \"string\") {\n throw new TelegramFileNotFoundError(\"Telegram did not return file content\");\n }\n const buffer = Buffer.from(data);\n control.onProgress?.({\n transferred: buffer.byteLength,\n total: buffer.byteLength,\n percent: 100,\n });\n return buffer;\n }\n\n async getStream(ref: StoredRef, control: TransferControl = {}): Promise<Readable> {\n // GramJS buffers internally; expose the result as a stream so callers can\n // pipe it without changing their code. Chunked iteration is a v0.3 concern.\n const data = await this.get(ref, control);\n return Readable.from([data]);\n }\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n async delete(ref: StoredRef): Promise<void> {\n try {\n await this.client.deleteMessages(this.peer, [Number(ref.messageId)], { revoke: true });\n } catch (error) {\n throw mapTelegramError(error, \"Telegram delete failed\");\n }\n }\n\n async exists(ref: StoredRef): Promise<boolean> {\n try {\n return (await this.stat(ref)) !== null;\n } catch (error) {\n throw mapTelegramError(error, \"Telegram lookup failed\");\n }\n }\n\n async stat(ref: StoredRef): Promise<StoredObject | null> {\n const message = await this.getMessage(ref.messageId);\n if (!message?.media) return null;\n\n const document = message.document;\n if (!(document instanceof Api.Document)) return null;\n\n let fileName: string | undefined;\n for (const attr of document.attributes) {\n if (attr instanceof Api.DocumentAttributeFilename) {\n fileName = attr.fileName;\n break;\n }\n }\n return {\n messageId: ref.messageId,\n name: fileName ?? \"file\",\n size: Number(document.size.toString()),\n mimeType: document.mimeType,\n };\n }\n\n // ── Health ───────────────────────────────────────────────────────────────\n\n /**\n * Validates that the client is connected and the storage channel is\n * reachable (a real Telegram round-trip on the channel peer).\n */\n async healthCheck(): Promise<StorageHealth> {\n const startedAt = Date.now();\n try {\n if (!this.client.connected) {\n return {\n healthy: false,\n latencyMs: null,\n message: \"Telegram client is not connected\",\n targetTitle: this.title,\n };\n }\n const result = await this.client.invoke(\n new Api.channels.GetFullChannel({ channel: this.peer }),\n );\n const title =\n result instanceof Api.messages.ChatFull && result.chats[0] instanceof Api.Channel\n ? result.chats[0].title\n : this.title;\n return {\n healthy: true,\n latencyMs: Date.now() - startedAt,\n message: null,\n targetTitle: title ?? null,\n };\n } catch (error) {\n const mapped = mapTelegramError(error, \"Telegram storage health check failed\");\n return {\n healthy: false,\n latencyMs: Date.now() - startedAt,\n message: mapped.message,\n targetTitle: this.title,\n };\n }\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw new OperationCancelledError();\n if (!this.client.connected) {\n throw new TelegramConnectionError(\"Telegram client is not connected\");\n }\n }\n\n private async materialize(input: StorageUploadInput): Promise<Buffer> {\n if (input.data) return input.data;\n if (!input.stream) {\n throw new UploadFailedError(\"Upload input has neither data nor a stream\");\n }\n const chunks: Buffer[] = [];\n for await (const chunk of input.stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as Uint8Array));\n }\n return Buffer.concat(chunks);\n }\n\n private documentSize(message: Api.Message): number | null {\n const document = message.document;\n if (document instanceof Api.Document) return Number(document.size.toString());\n return null;\n }\n\n private async requireMessage(ref: StoredRef): Promise<Api.Message> {\n const message = await this.getMessage(ref.messageId);\n if (!message?.media) {\n throw new TelegramFileNotFoundError();\n }\n return message;\n }\n\n private async getMessage(id: string): Promise<Api.Message | undefined> {\n try {\n const messages = await this.client.getMessages(this.peer, { ids: [Number(id)] });\n const first = messages[0];\n return first instanceof Api.Message ? first : undefined;\n } catch (error) {\n if (error instanceof TelegramAuthRequiredError) throw error;\n throw mapTelegramError(error, \"Telegram lookup failed\");\n }\n }\n}\n","import { Api, TelegramClient } from \"telegram\";\nimport { computeCheck } from \"telegram/Password.js\";\nimport {\n NotFoundError,\n TelegramConnectionError,\n ValidationError,\n type Repos,\n type StorageRecord,\n type UserRecord,\n} from \"@omnicloud/core\";\nimport {\n extractCreatedChannel,\n newTelegramClient,\n type TelegramClientManager,\n type TelegramCredentials,\n} from \"./client\";\nimport { isPasswordRequiredError, mapTelegramError } from \"./errors\";\n\nexport const TELEGRAM_PROVIDER = \"telegram\";\n\n/**\n * A Telegram login in progress: a connected client waiting for the\n * confirmation code (and possibly the 2FA password) to be supplied.\n */\ninterface LoginFlow {\n phone: string;\n client: TelegramClient;\n phoneCodeHash: string;\n createdAt: number;\n}\n\nconst LOGIN_FLOW_TTL_MS = 5 * 60 * 1000;\n\nexport type VerifyCodeResult = { status: \"ok\"; user: UserRecord } | { status: \"password_required\" };\n\n/**\n * Orchestrates the full Telegram account connection flow:\n * phone → code (→ 2FA password) → session persisted → user upserted →\n * private storage channel created.\n */\nexport class TelegramConnectionService {\n private flows = new Map<string, LoginFlow>();\n\n constructor(\n private readonly repos: Repos,\n private readonly clientManager: TelegramClientManager,\n private readonly credentials: TelegramCredentials,\n ) {}\n\n /** Sends the Telegram confirmation code for the phone number. */\n async startLogin(phone: string): Promise<void> {\n this.sweepExpiredFlows();\n await this.cancelLogin(phone);\n\n const client = newTelegramClient(this.credentials);\n try {\n await client.connect();\n const { phoneCodeHash } = await client.sendCode(\n { apiId: this.credentials.apiId, apiHash: this.credentials.apiHash },\n phone,\n );\n this.flows.set(phone, { phone, client, phoneCodeHash, createdAt: Date.now() });\n } catch (error) {\n await this.disconnectQuietly(client);\n throw mapTelegramError(error, \"Could not send the Telegram confirmation code\");\n }\n }\n\n /** Verifies the confirmation code. Returns `password_required` for 2FA accounts. */\n async verifyCode(phone: string, code: string): Promise<VerifyCodeResult> {\n const flow = this.requireFlow(phone);\n try {\n await flow.client.invoke(\n new Api.auth.SignIn({\n phoneNumber: flow.phone,\n phoneCodeHash: flow.phoneCodeHash,\n phoneCode: code,\n }),\n );\n } catch (error) {\n if (isPasswordRequiredError(error)) {\n return { status: \"password_required\" };\n }\n throw mapTelegramError(error, \"Telegram sign-in failed\");\n }\n return this.finalizeLogin(flow);\n }\n\n /** Completes sign-in for accounts with two-factor authentication enabled. */\n async verifyPassword(phone: string, password: string): Promise<VerifyCodeResult> {\n const flow = this.requireFlow(phone);\n try {\n const passwordInfo = await flow.client.invoke(new Api.account.GetPassword());\n const check = await computeCheck(passwordInfo, password);\n await flow.client.invoke(new Api.auth.CheckPassword({ password: check }));\n } catch (error) {\n throw mapTelegramError(error, \"Telegram sign-in failed\");\n }\n return this.finalizeLogin(flow);\n }\n\n /** Aborts a pending login flow (disconnects the client). */\n async cancelLogin(phone: string): Promise<void> {\n const flow = this.flows.get(phone);\n if (flow) {\n this.flows.delete(phone);\n await this.disconnectQuietly(flow.client);\n }\n }\n\n /**\n * Ensures the user has a registered private storage channel, creating it\n * on first use. Idempotent.\n */\n async ensureStorage(userId: string): Promise<StorageRecord> {\n const existing = await this.repos.storages.findByUserAndProvider(userId, TELEGRAM_PROVIDER);\n if (existing) return existing;\n\n const client = await this.clientManager.getForUser(userId);\n return this.ensureStorageForClient(userId, client);\n }\n\n async ensureStorageForClient(userId: string, client: TelegramClient): Promise<StorageRecord> {\n const existing = await this.repos.storages.findByUserAndProvider(userId, TELEGRAM_PROVIDER);\n if (existing) return existing;\n\n let channel: Api.Channel;\n try {\n const result = await client.invoke(\n new Api.channels.CreateChannel({\n title: \"OmniCloud Storage\",\n about:\n \"Private storage channel created by OmniCloud (https://github.com/Lacrous/OmniCloud)\",\n megagroup: false,\n }),\n );\n channel = extractCreatedChannel(result);\n } catch (error) {\n throw mapTelegramError(error, \"Could not create the private storage channel\");\n }\n\n const accessHash = channel.accessHash?.toString();\n if (!accessHash) {\n throw new TelegramConnectionError(\"Telegram did not provide the channel access hash\");\n }\n\n return this.repos.storages.create({\n userId,\n provider: TELEGRAM_PROVIDER,\n title: channel.title ?? \"OmniCloud Storage\",\n telegramChatId: channel.id.toString(),\n telegramAccessHash: accessHash,\n });\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private requireFlow(phone: string): LoginFlow {\n const flow = this.flows.get(phone);\n if (!flow) {\n throw new NotFoundError(\"No pending Telegram login for this phone number\");\n }\n if (Date.now() - flow.createdAt > LOGIN_FLOW_TTL_MS) {\n this.flows.delete(phone);\n void this.disconnectQuietly(flow.client);\n throw new ValidationError(\"The login session expired — please start again\");\n }\n return flow;\n }\n\n private async finalizeLogin(flow: LoginFlow): Promise<VerifyCodeResult> {\n this.flows.delete(flow.phone);\n\n let stringSession: string;\n let user: UserRecord;\n try {\n stringSession = flow.client.session.save() as unknown as string;\n const me = await flow.client.getMe();\n if (!(me instanceof Api.User)) {\n throw new Error(\"Telegram did not return the authenticated user\");\n }\n user = await this.repos.users.upsertFromTelegram({\n telegramUserId: me.id.toString(),\n username: me.username ?? null,\n firstName: me.firstName ?? null,\n lastName: me.lastName ?? null,\n phone: me.phone ?? flow.phone,\n });\n } catch (error) {\n await this.disconnectQuietly(flow.client);\n throw mapTelegramError(error, \"Could not complete the Telegram connection\");\n }\n\n await this.repos.sessions.save(user.id, stringSession);\n await this.ensureStorageForClient(user.id, flow.client);\n this.clientManager.cacheClient(user.id, flow.client);\n\n return { status: \"ok\", user };\n }\n\n private sweepExpiredFlows(): void {\n const now = Date.now();\n for (const [phone, flow] of this.flows) {\n if (now - flow.createdAt > LOGIN_FLOW_TTL_MS) {\n this.flows.delete(phone);\n void this.disconnectQuietly(flow.client);\n }\n }\n }\n\n private async disconnectQuietly(client: TelegramClient): Promise<void> {\n try {\n await client.disconnect();\n } catch {\n // best effort\n }\n }\n}\n"],"mappings":";AA4BO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YACkB,QACA,MAChB,SACgB,YAAgC,QAChC,UAAmB,QACnC;AACA,UAAM,OAAO;AANG;AACA;AAEA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EARkB;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAKpB;AA4DA,IAAM,gBAAwC,EAAE,UAAU,GAAG,aAAa,IAAI;AAE9E,SAAS,MAAM,IAAY,QAAqC;AAC9D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,YAAQ;AAAA,MACN;AAAA,MACA,MAAM;AACJ,qBAAa,KAAK;AAClB,eAAO,IAAI,eAAe,GAAG,WAAW,iBAAiB,CAAC;AAAA,MAC5D;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAEA,SAAS,kBAAkB,QAAyB;AAClD,SAAO,WAAW,OAAO,WAAW,OAAO,UAAU;AACvD;AAGA,SAAS,WAAW,OAA8C;AAChE,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,OAAO;AACT,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI;AAC3D,aAAO,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IAC/B;AAAA,EACF;AACA,QAAM,UAAU,OAAO,SAAS;AAChC,SAAO,UAAU,IAAI,OAAO,KAAK;AACnC;AAEA,SAAS,cAAc,MAAuB;AAC5C,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,IAAM,kBAAN,MAAsB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,UAAkC,CAAC,GAAG;AAChD,SAAK,WAAW,QAAQ,WAAW,IAAI,QAAQ,OAAO,EAAE;AACxD,SAAK,YAAY,QAAQ,aAAa,MAAM,KAAK,UAAU;AAC3D,SAAK,QAAQ,EAAE,GAAG,eAAe,GAAG,QAAQ,MAAM;AAClD,SAAK,iBAAiB;AAAA,MACpB,GAAI,QAAQ,QAAQ,EAAE,eAAe,UAAU,QAAQ,KAAK,GAAG,IAAI,CAAC;AAAA,MACpE,GAAG,QAAQ;AAAA,IACb;AAEA,SAAK,QAAQ,IAAI,SAAS,IAAI;AAC9B,SAAK,UAAU,IAAI,WAAW,IAAI;AAClC,SAAK,QAAQ,IAAI,SAAS,IAAI;AAC9B,SAAK,UAAU,IAAI,WAAW,IAAI;AAClC,SAAK,SAAS,IAAI,UAAU,IAAI;AAChC,SAAK,SAAS,IAAI,UAAU,IAAI;AAChC,SAAK,WAAW,IAAI,YAAY,IAAI;AACpC,SAAK,OAAO,IAAI,QAAQ,IAAI;AAAA,EAC9B;AAAA;AAAA,EAGA,MAAM,QAAW,QAAgB,MAAc,MAA4B;AACzE,WAAO,KAAK,UAAU,YAAY,KAAK,WAAc,QAAQ,MAAM,IAAI,CAAC;AAAA,EAC1E;AAAA;AAAA,EAGA,MAAM,WAAc,QAAgB,MAAc,MAA4B;AAC5E,UAAM,SAAS,OAAO,aAAa,eAAe,gBAAgB;AAClE,UAAM,WAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAC9D;AAAA,MACA,SAAS;AAAA,QACP,GAAG,KAAK;AAAA,QACR,GAAI,SAAS,UAAa,CAAC,SAAS,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MAChF;AAAA,MACA,MAAM,SAAS,SAAY,SAAY,SAAU,OAAoB,KAAK,UAAU,IAAI;AAAA,MACxF,aAAa;AAAA,IACf,CAAC;AAED,QAAI,SAAS,WAAW,IAAK,QAAO;AAEpC,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,OAAgB,OAAO,cAAc,IAAI,IAAI;AAEnD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,QACJ,MAGC;AACH,YAAM,IAAI;AAAA,QACR,SAAS;AAAA,QACT,OAAO,QAAQ;AAAA,QACf,OAAO,WAAW,mBAAmB,SAAS,MAAM;AAAA,QACpD,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,UAAa,WAAyC;AAC1D,UAAM,EAAE,UAAU,YAAY,IAAI,KAAK;AACvC,QAAI;AACJ,aAAS,UAAU,GAAG,WAAW,KAAK,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG;AACpE,UAAI;AACF,eAAO,MAAM,UAAU;AAAA,MACzB,SAAS,OAAO;AACd,oBAAY;AACZ,cAAM,YAAY,iBAAiB,kBAAkB,kBAAkB,MAAM,MAAM;AACnF,YAAI,CAAC,aAAa,WAAW,SAAU;AACvC,cAAM,MAAM,cAAc,MAAM,UAAU,EAAE;AAAA,MAC9C;AAAA,IACF;AACA,UAAM;AAAA,EACR;AAAA;AAAA,EAGA,MAAM,cACJ,MACA,YACyB;AACzB,UAAM,WAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAC9D,SAAS,KAAK;AAAA,MACd,aAAa;AAAA,IACf,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,QAAS,cAAc,IAAI,GAC7B;AACJ,YAAM,IAAI;AAAA,QACR,SAAS;AAAA,QACT,OAAO,QAAQ;AAAA,QACf,OAAO,WAAW,oBAAoB,SAAS,MAAM;AAAA,MACvD;AAAA,IACF;AAEA,UAAM,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,CAAC,KAAK;AACrE,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,UAAM,SAAS,SAAS,QAAQ,IAAI,kBAAkB;AACtD,UAAM,kBAAkB,SAAS,QAAQ,IAAI,sBAAsB;AACnE,UAAM,oBAAoB,oBAAoB,OAAO,OAAO,oBAAoB;AAEhF,UAAM,OAAO,MAAM,SAAS,UAAU,OAAO,UAAU;AACvD,WAAO,EAAE,MAAM,aAAa,MAAM,KAAK,YAAY,QAAQ,kBAAkB;AAAA,EAC/E;AACF;AAGA,eAAe,SACb,UACA,OACA,YAC8B;AAC9B,QAAM,OAAO,SAAS;AACtB,MAAI,CAAC,MAAM;AACT,UAAM,SAAS,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC;AAC1D,iBAAa,EAAE,QAAQ,OAAO,YAAY,OAAO,OAAO,YAAY,SAAS,IAAI,CAAC;AAClF,WAAO;AAAA,EACT;AAEA,QAAM,SAAuB,CAAC;AAC9B,MAAI,SAAS;AACb,QAAM,SAAS,KAAK,UAAU;AAC9B,aAAS;AACP,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,QAAI,OAAO;AACT,aAAO,KAAK,KAAK;AACjB,gBAAU,MAAM;AAChB,mBAAa;AAAA,QACX;AAAA,QACA;AAAA,QACA,SAAS,QAAQ,KAAK,MAAO,SAAS,QAAS,GAAG,IAAI;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,WAAW,MAAM;AACpC,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AAC1B,WAAO,IAAI,OAAO,MAAM;AACxB,cAAU,MAAM;AAAA,EAClB;AAEA,QAAM,aAAc,WAA0C;AAC9D,SAAO,aAAa,WAAW,KAAK,MAAM,IAAI;AAChD;AAMA,IAAM,WAAN,MAAe;AAAA,EACb,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA;AAAA,EAG7B,MAAM,KAAK,QAAmB,CAAC,GAA0B;AACvD,WAAO,KAAK,OAAO,QAAsB,OAAO,aAAa,WAAW,KAAK,CAAC,EAAE;AAAA,EAClF;AAAA,EAEA,MAAM,IAAI,IAA8B;AACtC,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO,QAA2B,OAAO,cAAc,EAAE,EAAE;AACvF,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,IAAuC;AACpD,UAAM,EAAE,SAAS,IAAI,MAAM,KAAK,OAAO;AAAA,MACrC;AAAA,MACA,cAAc,EAAE;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,OAAoB,UAAyB,CAAC,GAAqB;AAC9E,UAAM,WAAW,KAAK,IAAI,IAAI,QAAQ,SAAS,KAAK,CAAC;AACrD,QAAI;AAEJ,aAAS,UAAU,GAAG,WAAW,UAAU,WAAW,GAAG;AACvD,UAAI;AACF,eAAO,MAAM,KAAK,WAAW,OAAO,OAAO;AAAA,MAC7C,SAAS,OAAO;AACd,oBAAY;AACZ,YAAI,QAAQ,QAAQ,QAAS,OAAM;AACnC,YAAI,iBAAiB,kBAAkB,CAAC,kBAAkB,MAAM,MAAM,EAAG,OAAM;AAC/E,YAAI,UAAU,SAAU,OAAM,MAAM,MAAM,MAAM,UAAU,IAAI,QAAQ,MAAM;AAAA,MAC9E;AAAA,IACF;AACA,UAAM;AAAA,EACR;AAAA,EAEQ,WAAW,OAAoB,SAA0C;AAC/E,UAAM,OAAO,IAAI,SAAS;AAC1B,UAAM,OACJ,OAAO,SAAS,eAAe,MAAM,gBAAgB,OACjD,MAAM,OACN,IAAI,KAAK,CAAC,IAAI,WAAW,MAAM,IAAkB,CAAC,CAAC;AACzD,SAAK,OAAO,QAAQ,MAAM,MAAM,IAAI;AACpC,QAAI,QAAQ,SAAU,MAAK,OAAO,YAAY,QAAQ,QAAQ;AAE9D,QAAI,OAAO,mBAAmB,YAAY;AACxC,aAAO,KAAK,aAAa,MAAM,OAAO;AAAA,IACxC;AACA,WAAO,KAAK,OACT,QAA2B,QAAQ,cAAc,IAAI,EACrD,KAAK,CAAC,aAAa,SAAS,IAAI;AAAA,EACrC;AAAA,EAEQ,aAAa,MAAgB,SAA0C;AAC7E,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,MAAM,IAAI,eAAe;AAC/B,UAAI,KAAK,QAAQ,GAAG,KAAK,OAAO,OAAO,YAAY;AACnD,UAAI,eAAe;AACnB,UAAI,kBAAkB;AAEtB,YAAM,UAAU,MAAM,IAAI,MAAM;AAChC,cAAQ,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAEjE,UAAI,OAAO,aAAa,CAAC,UAAU;AACjC,gBAAQ,aAAa;AAAA,UACnB,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,SAAS,MAAM,QAAQ,IAAI,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG,IAAI;AAAA,QAC9E,CAAC;AAAA,MACH;AACA,UAAI,SAAS,MAAM;AACjB,gBAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,cAAM,OAAO,IAAI;AAIjB,YAAI,IAAI,UAAU,OAAO,IAAI,SAAS,OAAO,MAAM,MAAM;AACvD,kBAAQ,aAAa,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,IAAI,CAAC;AAC1D,kBAAQ,KAAK,IAAI;AACjB;AAAA,QACF;AACA;AAAA,UACE,IAAI;AAAA,YACF,IAAI;AAAA,YACJ,MAAM,OAAO,QAAQ;AAAA,YACrB,MAAM,OAAO,WAAW,kBAAkB,IAAI,MAAM;AAAA,YACpD,MAAM,OAAO;AAAA,UACf;AAAA,QACF;AAAA,MACF;AACA,UAAI,UAAU,MACZ,OAAO,IAAI,eAAe,GAAG,iBAAiB,6BAA6B,CAAC;AAC9E,UAAI,UAAU,MAAM,OAAO,IAAI,eAAe,GAAG,WAAW,gBAAgB,CAAC;AAC7E,UAAI,KAAK,IAAI;AAAA,IACf,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,QAAQ,IAAY,OAAsC;AAC9D,UAAM,OAAO,IAAI,SAAS;AAC1B,UAAM,OACJ,OAAO,SAAS,eAAe,MAAM,gBAAgB,OACjD,MAAM,OACN,IAAI,KAAK,CAAC,IAAI,WAAW,MAAM,IAAkB,CAAC,CAAC;AACzD,SAAK,OAAO,QAAQ,MAAM,MAAM,IAAI;AACpC,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO;AAAA,MACjC;AAAA,MACA,cAAc,EAAE;AAAA,MAChB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,IAAY,YAAqE;AAC9F,WAAO,KAAK,OAAO,cAAc,cAAc,EAAE,aAAa,UAAU;AAAA,EAC1E;AAAA;AAAA,EAGA,YAAY,IAAoB;AAC9B,WAAO,GAAG,KAAK,OAAO,OAAO,cAAc,EAAE;AAAA,EAC/C;AAAA,EAEA,MAAM,OAAO,IAAY,MAAgC;AACvD,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO,QAA2B,SAAS,cAAc,EAAE,IAAI;AAAA,MACzF;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,IAAY,UAAU,MAAwB;AACvD,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO,QAA2B,SAAS,cAAc,EAAE,IAAI;AAAA,MACzF;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,IAAY,UAA2C;AAChE,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO,QAA2B,QAAQ,cAAc,EAAE,SAAS;AAAA,MAC7F;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,IAA8B;AACxC,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO,QAA2B,QAAQ,cAAc,EAAE,QAAQ;AAC9F,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,IAA8B;AAC1C,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,OAAO;AAAA,MACjC;AAAA,MACA,cAAc,EAAE;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,OAAO,IAA2B;AACtC,UAAM,KAAK,OAAO,QAAc,UAAU,cAAc,EAAE,EAAE;AAAA,EAC9D;AAAA;AAAA,EAGA,MAAM,MACJ,WACA,KACA,UAAwC,CAAC,GAChB;AACzB,WAAO,KAAK,OAAO,QAAwB,QAAQ,oBAAoB;AAAA,MACrE;AAAA,MACA;AAAA,MACA,GAAI,cAAc,SAAS,EAAE,UAAU,QAAQ,YAAY,KAAK,IAAI,CAAC;AAAA,IACvE,CAAC;AAAA,EACH;AACF;AAEA,IAAM,aAAN,MAAiB;AAAA,EACf,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA,EAE7B,MAAM,KAAK,QAAkD,CAAC,GAA4B;AACxF,UAAM,EAAE,UAAU,GAAG,KAAK,IAAI;AAC9B,UAAM,OACJ,aAAa,SACT,yBAAyB,mBAAmB,YAAY,EAAE,CAAC,KAC3D,eAAe,WAAW,IAAI,CAAC;AACrC,WAAO,KAAK,OAAO,QAAwB,OAAO,IAAI;AAAA,EACxD;AAAA;AAAA,EAGA,MAAM,OAA6B;AACjC,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,OAAO;AAAA,MACpC;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,MAAc,WAA0B,MAA0B;AAC7E,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO,QAA+B,QAAQ,gBAAgB;AAAA,MAC1F;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,IAAY,MAAkC;AACzD,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO;AAAA,MACnC;AAAA,MACA,gBAAgB,EAAE;AAAA,MAClB,EAAE,KAAK;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,IAAY,UAAU,MAA0B;AACzD,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO;AAAA,MACnC;AAAA,MACA,gBAAgB,EAAE;AAAA,MAClB,EAAE,QAAQ;AAAA,IACZ;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,IAAY,UAA6C;AAClE,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO;AAAA,MACnC;AAAA,MACA,gBAAgB,EAAE;AAAA,MAClB,EAAE,SAAS;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,IAA8C;AACxD,WAAO,KAAK,OAAO,QAAiC,QAAQ,gBAAgB,EAAE,QAAQ;AAAA,EACxF;AAAA,EAEA,MAAM,QAAQ,IAA8C;AAC1D,WAAO,KAAK,OAAO,QAAiC,QAAQ,gBAAgB,EAAE,UAAU;AAAA,EAC1F;AAAA;AAAA,EAGA,MAAM,OAAO,IAA8C;AACzD,WAAO,KAAK,OAAO,QAAiC,UAAU,gBAAgB,EAAE,EAAE;AAAA,EACpF;AAAA,EAEA,MAAM,MACJ,WACA,KACA,UAAwC,CAAC,GAChB;AACzB,WAAO,KAAK,OAAO,QAAwB,QAAQ,sBAAsB;AAAA,MACvE;AAAA,MACA;AAAA,MACA,GAAI,cAAc,SAAS,EAAE,UAAU,QAAQ,YAAY,KAAK,IAAI,CAAC;AAAA,IACvE,CAAC;AAAA,EACH;AACF;AAEA,IAAM,WAAN,MAAe;AAAA,EACb,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA,EAE7B,MAAM,KAAK,QAAmB,CAAC,GAA0B;AACvD,WAAO,KAAK,OAAO,QAAsB,OAAO,aAAa,WAAW,KAAK,CAAC,EAAE;AAAA,EAClF;AAAA;AAAA,EAGA,MAAM,QAAsC;AAC1C,WAAO,KAAK,OAAO,QAA6B,QAAQ,kBAAkB;AAAA,EAC5E;AACF;AAEA,IAAM,aAAN,MAAiB;AAAA,EACf,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA;AAAA,EAG7B,MAAM,SAA8B;AAClC,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,OAAO;AAAA,MACpC;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAkC;AACtC,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,OAAO;AAAA,MAClC;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAwE;AAC5E,WAAO,KAAK,OAAO,QAAQ,OAAO,qBAAqB;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,MAAM,OAAO,OAAkC;AACnD,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO;AAAA,MACnC;AAAA,MACA;AAAA,MACA,EAAE,KAAK;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,eAAe,UAA8B,CAAC,GAAgC;AAClF,UAAM,EAAE,OAAO,IAAI,MAAM,KAAK,OAAO;AAAA,MACnC;AAAA,MACA;AAAA,MACA,EAAE,MAAM,QAAQ,QAAQ,MAAM;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,eAAmC;AACvC,WAAO,KAAK,OAAO,QAAmB,OAAO,aAAa;AAAA,EAC5D;AACF;AAEA,IAAM,YAAN,MAAgB;AAAA,EACd,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,MAAM,MAAM,GAAW,OAA0C,CAAC,GAA6B;AAC7F,WAAO,KAAK,OAAO;AAAA,MACjB;AAAA,MACA,cAAc,WAAW,EAAE,GAAG,GAAG,KAAK,CAAU,CAAC;AAAA,IACnD;AAAA,EACF;AACF;AAEA,IAAM,YAAN,MAAgB;AAAA,EACd,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA,EAE7B,MAAM,KAAK,OAA0C,CAAC,GAA2B;AAC/E,WAAO,KAAK,OAAO,QAAuB,OAAO,cAAc,WAAW,IAAa,CAAC,EAAE;AAAA,EAC5F;AACF;AAEA,IAAM,cAAN,MAAkB;AAAA,EAChB,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA,EAE7B,MAAM,KAAK,OAA0C,CAAC,GAA6B;AACjF,WAAO,KAAK,OAAO,QAAyB,OAAO,gBAAgB,WAAW,IAAa,CAAC,EAAE;AAAA,EAChG;AACF;AAEA,IAAM,UAAN,MAAc;AAAA,EACZ,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA,EAE7B,MAAM,KAA2B;AAC/B,WAAO,KAAK,OAAO,QAAqB,OAAO,cAAc;AAAA,EAC/D;AAAA,EAEA,MAAM,mBAAmB,OAA8B;AACrD,UAAM,KAAK,OAAO,QAAQ,QAAQ,4BAA4B,EAAE,MAAM,CAAC;AAAA,EACzE;AAAA,EAEA,MAAM,mBAAmB,OAAe,MAA+C;AACrF,WAAO,KAAK,OAAO,QAAgC,QAAQ,6BAA6B;AAAA,MACtF;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,uBACJ,OACA,UAC0C;AAC1C,WAAO,KAAK,OAAO,QAAQ,QAAQ,+BAA+B,EAAE,OAAO,SAAS,CAAC;AAAA,EACvF;AAAA,EAEA,MAAM,SAAwB;AAC5B,UAAM,KAAK,OAAO,QAAQ,QAAQ,kBAAkB;AAAA,EACtD;AACF;;;AC5rBA,IAAM,oBAA4C;AAAA;AAAA,EAEhD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA;AAAA,EAGN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAGL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA;AAAA,EAGJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEO,IAAM,oBAAoB;AAG1B,SAAS,cAAc,UAA0B;AACtD,QAAM,OAAO,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK;AAC9C,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI,OAAO,KAAK,QAAQ,KAAK,SAAS,EAAG,QAAO;AAChD,SAAO,KAAK,MAAM,MAAM,CAAC,EAAE,YAAY;AACzC;AAGO,SAAS,iBAAiB,UAA0B;AACzD,SAAO,kBAAkB,cAAc,QAAQ,CAAC,KAAK;AACvD;AAKA,IAAM,sBAAqD;AAAA,EACzD,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,UAAU,OAAO;AAAA,EAClB,CAAC,mBAAmB,KAAK;AAAA,EACzB,CAAC,mBAAmB,SAAS;AAAA,EAC7B,CAAC,qBAAqB,SAAS;AAAA,EAC/B,CAAC,oBAAoB,SAAS;AAAA,EAC9B,CAAC,+BAA+B,SAAS;AAAA,EACzC,CAAC,uBAAuB,SAAS;AAAA,EACjC,CAAC,uBAAuB,SAAS;AAAA,EACjC,CAAC,oBAAoB,SAAS;AAAA,EAC9B,CAAC,SAAS,MAAM;AAAA,EAChB,CAAC,oBAAoB,MAAM;AAAA,EAC3B,CAAC,mBAAmB,MAAM;AAAA,EAC1B,CAAC,oBAAoB,MAAM;AAAA,EAC3B,CAAC,0BAA0B,MAAM;AAAA,EACjC,CAAC,iBAAiB,MAAM;AAC1B;AAEA,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,aAAa,UAAgC;AAC3D,aAAW,CAAC,QAAQ,QAAQ,KAAK,qBAAqB;AACpD,QAAI,SAAS,WAAW,MAAM,EAAG,QAAO;AAAA,EAC1C;AACA,MAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,SAAO;AACT;AAEO,IAAM,kBAA2C;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,oBAA6D;AAAA,EACjE,OAAO,CAAC,QAAQ;AAAA,EAChB,OAAO,CAAC,QAAQ;AAAA,EAChB,OAAO,CAAC,QAAQ;AAAA,EAChB,KAAK,CAAC,iBAAiB;AAAA,EACvB,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,UAAU,CAAC,GAAG,YAAY;AAAA,EAC1B,OAAO,CAAC;AACV;AAEO,SAAS,eAAe,OAAsC;AACnE,SAAQ,gBAAsC,SAAS,KAAK;AAC9D;AASO,SAAS,oBAAoB,OAAyC;AAC3E,QAAM,aAAa,MAAM,YAAY,EAAE,KAAK;AAC5C,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI,eAAe,UAAU,GAAG;AAC9B,UAAM,WAAW,kBAAkB,UAAU;AAC7C,WAAO,SAAS,SAAS,IAAI,WAAW;AAAA,EAC1C;AAEA,SAAO,CAAC,UAAU;AACpB;;;ACjJA,IAAM,aAAqC;AAAA,EACzC,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,IAAI,QAAQ;AAAA,EACZ,IAAI,QAAQ;AAAA,EACZ,IAAI,QAAQ;AACd;AAGO,SAAS,UAAU,OAA8B;AACtD,QAAM,QAAQ,8CAA8C,KAAK,KAAK;AACtE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,OAAO,MAAM,CAAC,CAAC;AAC9B,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,EAAG,QAAO;AACnD,QAAM,QAAQ,MAAM,CAAC,KAAK,IAAI,YAAY,KAAK;AAC/C,QAAM,SAAS,WAAW,IAAI;AAC9B,MAAI,WAAW,OAAW,QAAO;AACjC,SAAO,KAAK,MAAM,SAAS,MAAM;AACnC;AAGO,SAAS,oBAAoB,OAAsD;AACxF,QAAM,QAAQ,6BAA6B,KAAK,KAAK;AACrD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,QAAM,QAAQ,UAAU,MAAM,CAAC,CAAE;AACjC,MAAI,UAAU,KAAM,QAAO;AAE3B,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,EAAE,KAAK,QAAQ,EAAE;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,KAAK,MAAM;AAAA,IACtB,KAAK;AACH,aAAO,EAAE,KAAK,QAAQ,EAAE;AAAA,IAC1B,KAAK;AACH,aAAO,EAAE,KAAK,MAAM;AAAA,IACtB;AACE,aAAO,EAAE,KAAK,OAAO,KAAK,MAAM;AAAA,EACpC;AACF;AAEA,SAAS,aAAa,OAA+B;AACnD,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,CAAC,QAAQ,KAAK,OAAO,KAAK,IAAI,EAAE,SAAS,UAAU,EAAG,QAAO;AACjE,MAAI,CAAC,SAAS,KAAK,MAAM,KAAK,KAAK,EAAE,SAAS,UAAU,EAAG,QAAO;AAClE,SAAO;AACT;AAEA,SAAS,UAAU,OAAe,UAAgC;AAChE,QAAM,UAAU,MAAM,KAAK;AAG3B,MAAI,sBAAsB,KAAK,OAAO,GAAG;AACvC,UAAMA,QAAO,oBAAI,KAAK,GAAG,OAAO,IAAI,WAAW,iBAAiB,cAAc,GAAG;AACjF,WAAO,OAAO,MAAMA,MAAK,QAAQ,CAAC,IAAI,OAAOA;AAAA,EAC/C;AACA,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAGO,SAAS,iBACd,OACA,OAAkB,CAAC,GACqB;AACxC,QAAM,QAAmB,EAAE,GAAG,KAAK;AACnC,QAAM,QAAkB,CAAC;AAEzB,aAAW,YAAY,MAAM,KAAK,EAAE,MAAM,KAAK,GAAG;AAChD,QAAI,CAAC,SAAU;AAEf,UAAM,YAAY,SAAS,QAAQ,GAAG;AACtC,QAAI,aAAa,GAAG;AAClB,YAAM,KAAK,QAAQ;AACnB;AAAA,IACF;AAEA,UAAM,QAAQ,SAAS,MAAM,GAAG,SAAS,EAAE,YAAY;AACvD,UAAM,QAAQ,SAAS,MAAM,YAAY,CAAC;AAC1C,QAAI,CAAC,OAAO;AACV,YAAM,KAAK,QAAQ;AACnB;AAAA,IACF;AAEA,YAAQ,OAAO;AAAA,MACb,KAAK;AAAA,MACL,KAAK;AACH,cAAM,KAAK,KAAK;AAChB;AAAA,MACF,KAAK;AAEH,cAAM,OAAO,MAAM,YAAY;AAC/B;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,cAAM,MAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,YAAY;AACjD;AAAA,MACF,KAAK,QAAQ;AACX,cAAM,aAAa,oBAAoB,KAAK;AAC5C,YAAI,CAAC,YAAY;AACf,gBAAM,KAAK,QAAQ;AACnB;AAAA,QACF;AACA,YAAI,WAAW,QAAQ,OAAW,OAAM,UAAU,WAAW;AAC7D,YAAI,WAAW,QAAQ,OAAW,OAAM,UAAU,WAAW;AAC7D;AAAA,MACF;AAAA,MACA,KAAK;AACH,cAAM,aAAa;AACnB;AAAA,MACF,KAAK,WAAW;AACd,cAAM,SAAS,aAAa,KAAK;AACjC,YAAI,WAAW,KAAM,OAAM,KAAK,QAAQ;AAAA,YACnC,OAAM,UAAU;AACrB;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK,WAAW;AACd,cAAM,SAAS,aAAa,KAAK;AACjC,YAAI,WAAW,KAAM,OAAM,KAAK,QAAQ;AAAA,YACnC,OAAM,SAAS,SAAS,YAAY;AACzC;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK,SAAS;AACZ,cAAM,OAAO,UAAU,OAAO,KAAK;AACnC,YAAI,CAAC,KAAM,OAAM,KAAK,QAAQ;AAAA,YACzB,OAAM,OAAO,KAAK,YAAY;AACnC;AAAA,MACF;AAAA,MACA,KAAK,UAAU;AACb,cAAM,OAAO,UAAU,OAAO,IAAI;AAClC,YAAI,CAAC,KAAM,OAAM,KAAK,QAAQ;AAAA,YACzB,OAAM,KAAK,KAAK,YAAY;AACjC;AAAA,MACF;AAAA,MACA;AACE,cAAM,KAAK,QAAQ;AACnB;AAAA,IACJ;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,KAAK,GAAG,EAAE,KAAK;AACtC,MAAI,SAAU,OAAM,IAAI;AACxB,SAAO,EAAE,OAAO,SAAS;AAC3B;AAyBO,SAAS,eAAe,MAAc,OAAe,OAAe;AACzE,SAAO,EAAE,MAAM,OAAO,OAAO,SAAS,OAAO,QAAQ,MAAM;AAC7D;;;AC2DO,IAAM,cAAc;AAAA,EACzB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,yBAAyB;AAAA,EACzB,qBAAqB;AAAA,EACrB,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,yBAAyB;AAAA,EACzB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,UAAU;AACZ;;;AC1SO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAAiB,QAAgB,SAAiB,SAAmB;AAC/E,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AACvB,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;AAGO,IAAM,kBAAN,cAA8B,YAAY;AAAA,EAC/C,YAAY,SAAiB,SAAmB;AAC9C,UAAM,YAAY,iBAAiB,KAAK,SAAS,OAAO;AAAA,EAC1D;AACF;AAGO,IAAM,oBAAN,cAAgC,YAAY;AAAA,EACjD,YAAY,UAAU,2BAA2B;AAC/C,UAAM,YAAY,eAAe,KAAK,OAAO;AAAA,EAC/C;AACF;AAUO,IAAM,iBAAN,cAA6B,YAAY;AAAA,EAC9C,YAAY,UAAU,2CAA2C;AAC/D,UAAM,YAAY,mBAAmB,KAAK,OAAO;AAAA,EACnD;AACF;AAMO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAC7C,YAAY,UAAU,sBAAsB,UAA8B;AACxE;AAAA,MACE,aAAa,SACT,YAAY,iBACZ,aAAa,WACX,YAAY,mBACZ,YAAY;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAC7C,YAAY,SAAiB;AAC3B,UAAM,YAAY,UAAU,KAAK,OAAO;AAAA,EAC1C;AACF;AAQO,IAAM,mBAAN,cAA+B,YAAY;AAAA,EAChD,YAAY,UAAU,yCAAyC;AAC7D,UAAM,YAAY,cAAc,KAAK,OAAO;AAAA,EAC9C;AACF;AAGO,IAAM,6BAAN,cAAyC,YAAY;AAAA,EAC1D,cAAc;AACZ,UAAM,YAAY,yBAAyB,KAAK,6CAA6C;AAAA,EAC/F;AACF;AAUO,IAAM,4BAAN,cAAwC,YAAY;AAAA,EACzD,YAAY,UAAU,6DAAwD;AAC5E,UAAM,YAAY,wBAAwB,KAAK,OAAO;AAAA,EACxD;AACF;AAGO,IAAM,0BAAN,cAAsC,YAAY;AAAA,EACvD,YAAY,UAAU,4BAA4B,SAAmB;AACnE,UAAM,YAAY,4BAA4B,KAAK,SAAS,OAAO;AAAA,EACrE;AACF;AAGO,IAAM,4BAAN,cAAwC,YAAY;AAAA,EACzD,YAAY,UAAU,4CAA4C;AAChE,UAAM,YAAY,yBAAyB,KAAK,OAAO;AAAA,EACzD;AACF;AAGO,IAAM,oBAAN,cAAgC,YAAY;AAAA,EACjD,YAAY,UAAU,iBAAiB,SAAmB;AACxD,UAAM,YAAY,eAAe,KAAK,SAAS,OAAO;AAAA,EACxD;AACF;AAiBO,IAAM,0BAAN,cAAsC,YAAY;AAAA,EACvD,YAAY,UAAU,uBAAuB;AAC3C,UAAM,YAAY,iBAAiB,KAAK,OAAO;AAAA,EACjD;AACF;AAkBO,SAAS,iBAAiB,SAAiB,OAA6B;AAC7E,MAAI,iBAAiB,YAAa,QAAO;AACzC,QAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,SAAO,IAAI,kBAAkB,SAAS,GAAG,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK;AAChF;;;ACxKA,SAAS,gBAAgB;;;ACAzB,SAAS,kBAAkB;AAEpB,SAAS,UAAU,MAA0B;AAClD,SAAO,WAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvD;;;ADuBO,IAAM,uBAA0C,EAAE,UAAU,GAAG,aAAa,IAAI;AAGvF,SAAS,YAAY,OAAyB;AAC5C,MAAI,iBAAiB,wBAAyB,QAAO;AACrD,QAAM,OAAO,iBAAiB,QAAQ,MAAM,OAAO;AACnD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,QAAM,OAAQ,MAA4B;AAE1C,MAAI,SAAS,gBAAgB,SAAS,YAAa,QAAO;AAE1D,MAAI,oDAAoD,KAAK,OAAO,EAAG,QAAO;AAC9E,SAAO;AACT;AAEA,SAAS,MAAM,IAAY,QAAqC;AAC9D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,SAAS;AACnB,aAAO,IAAI,wBAAwB,CAAC;AACpC;AAAA,IACF;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,cAAQ;AAAA,IACV,GAAG,EAAE;AACL,aAAS,UAAU;AACjB,mBAAa,KAAK;AAClB,aAAO,IAAI,wBAAwB,CAAC;AAAA,IACtC;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAC3D,CAAC;AACH;AAEA,SAAS,eAAe,QAA4B;AAClD,MAAI,QAAQ,QAAS,OAAM,IAAI,wBAAwB;AACzD;AAeO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACmB,UACA,cAAiC,sBAClD;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,IAAI,eAAuB;AACzB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OACJ,OACA,UAA2B,CAAC,GACC;AAC7B,UAAM,OAAO,MAAM,KAAK,UAAU,KAAK;AAEvC,UAAM,SAAS,UAAU,IAAI;AAC7B,YAAQ,aAAa,EAAE,aAAa,GAAG,OAAO,KAAK,YAAY,SAAS,EAAE,CAAC;AAE3E,UAAM,SAAS,MAAM,KAAK;AAAA,MACxB,MAAM,KAAK,SAAS,IAAI,EAAE,GAAG,OAAO,KAAK,GAAG,OAAO;AAAA,MACnD;AAAA,MACA,CAAC,UAAU,IAAI,kBAAkB,iBAAiB,KAAK;AAAA,IACzD;AAEA,YAAQ,aAAa,EAAE,aAAa,KAAK,YAAY,OAAO,KAAK,YAAY,SAAS,IAAI,CAAC;AAC3F,WAAO,EAAE,QAAQ,QAAQ,MAAM,KAAK,WAAW;AAAA,EACjD;AAAA,EAEA,MAAM,SAAS,KAAgB,UAA2B,CAAC,GAAoB;AAC7E,WAAO,KAAK;AAAA,MACV,MAAM,KAAK,SAAS,IAAI,KAAK,OAAO;AAAA,MACpC;AAAA,MACA,CAAC,UAAU,iBAAiB,mBAAmB,KAAK;AAAA,IACtD;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,eACJ,KACA,UAA2B,CAAC,GAC8B;AAC1D,UAAM,SAAS,MAAM,KAAK;AAAA,MACxB,MAAM,KAAK,SAAS,UAAU,KAAK,OAAO;AAAA,MAC1C;AAAA,MACA,CAAC,UAAU,iBAAiB,mBAAmB,KAAK;AAAA,IACtD;AACA,WAAO,EAAE,QAAQ,MAAM,MAAM,KAAK;AAAA,EACpC;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC1C,UAAM,KAAK;AAAA,MACT,MAAM,KAAK,SAAS,OAAO,GAAG;AAAA,MAC9B,CAAC;AAAA,MACD,CAAC,UAAU,iBAAiB,iBAAiB,KAAK;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAAkC;AAC7C,QAAI;AACF,aAAO,MAAM,KAAK,SAAS,OAAO,GAAG;AAAA,IACvC,SAAS,OAAO;AACd,YAAM,iBAAiB,yBAAyB,KAAK;AAAA,IACvD;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAA8C;AACvD,QAAI;AACF,aAAO,MAAM,KAAK,SAAS,KAAK,GAAG;AAAA,IACrC,SAAS,OAAO;AACd,YAAM,iBAAiB,yBAAyB,KAAK;AAAA,IACvD;AAAA,EACF;AAAA,EAEA,MAAM,SAAiC;AACrC,QAAI;AACF,aAAO,MAAM,KAAK,SAAS,YAAY;AAAA,IACzC,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW;AAAA,QACX,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAClD,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,MAAc,gBAA0C;AAC5E,WAAO,UAAU,IAAI,MAAM;AAAA,EAC7B;AAAA;AAAA;AAAA,EAKA,MAAc,UAAU,OAA4C;AAClE,QAAI,MAAM,KAAM,QAAO,MAAM;AAC7B,QAAI,CAAC,MAAM,QAAQ;AACjB,YAAM,IAAI,kBAAkB,4CAA4C;AAAA,IAC1E;AACA,UAAM,SAAmB,CAAC;AAC1B,qBAAiB,SAAS,MAAM,QAAQ;AACtC,aAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAmB,CAAC;AAAA,IAC/E;AACA,WAAO,OAAO,OAAO,MAAM;AAAA,EAC7B;AAAA,EAEA,MAAc,UACZ,WACA,SACA,WACY;AACZ,UAAM,EAAE,UAAU,YAAY,IAAI,KAAK;AACvC,QAAI;AAEJ,aAAS,UAAU,GAAG,WAAW,KAAK,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG;AACpE,qBAAe,QAAQ,MAAM;AAC7B,UAAI;AACF,eAAO,MAAM,UAAU;AAAA,MACzB,SAAS,OAAO;AACd,oBAAY;AACZ,YAAI,WAAW,YAAY,CAAC,YAAY,KAAK,EAAG;AAChD,cAAM,MAAM,cAAc,MAAM,UAAU,IAAI,QAAQ,MAAM;AAAA,MAC9D;AAAA,IACF;AACA,UAAM,UAAU,SAAS;AAAA,EAC3B;AACF;;;AErMA,eAAsB,iBACpB,SACA,QACA,OACoB;AACpB,QAAM,QAAmB,CAAC;AAI1B,MAAI,MAAM,aAAa,OAAW,OAAM,WAAW,MAAM;AACzD,MAAI,MAAM,KAAK,MAAM,EAAE,KAAK,EAAG,OAAM,eAAe,MAAM,EAAE,KAAK;AACjE,MAAI,MAAM,IAAK,OAAM,MAAM,MAAM,IAAI,QAAQ,OAAO,EAAE,EAAE,YAAY;AACpE,MAAI,MAAM,YAAY,OAAW,OAAM,UAAU,MAAM;AACvD,MAAI,MAAM,YAAY,OAAW,OAAM,UAAU,MAAM;AACvD,MAAI,MAAM,YAAY,OAAW,OAAM,UAAU,MAAM;AACvD,MAAI,MAAM,KAAM,OAAM,OAAO,MAAM;AACnC,MAAI,MAAM,MAAO,OAAM,QAAQ,MAAM;AAErC,UAAQ,MAAM,QAAQ;AAAA,IACpB,KAAK;AACH,YAAM,UAAU;AAChB;AAAA,IACF,KAAK;AACH,YAAM,UAAU;AAChB;AAAA,IACF;AACE,YAAM,UAAU;AAChB;AAAA,EACJ;AAEA,MAAI,MAAM,MAAM;AACd,UAAM,WAAW,oBAAoB,MAAM,IAAI;AAC/C,QAAI,SAAU,OAAM,eAAe;AAAA,EACrC;AAEA,MAAI,MAAM,MAAM;AACd,UAAM,OAAO,IAAI,KAAK,MAAM,IAAI;AAChC,QAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,OAAM,OAAO;AAAA,EAClD;AACA,MAAI,MAAM,IAAI;AACZ,UAAM,KAAK,IAAI,KAAK,MAAM,EAAE;AAC5B,QAAI,CAAC,OAAO,MAAM,GAAG,QAAQ,CAAC,EAAG,OAAM,KAAK;AAAA,EAC9C;AAEA,MAAI,MAAM,YAAY;AACpB,UAAM,YAAY,MAAM,QAAQ,cAAc,QAAQ,MAAM,UAAU;AAAA,EACxE;AAEA,SAAO;AACT;;;ACpCO,IAAM,kBAAN,MAAkD;AAAA,EACvD,YACmB,UAEA,gBAAgB,IACjC;AAHiB;AAEA;AAAA,EAChB;AAAA,EAHgB;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnB,MAAM,OAAO,OAA2C;AACtD,QAAI;AACF,YAAM,KAAK,SAAS,OAAO;AAAA,QACzB,QAAQ,MAAM;AAAA,QACd,QAAQ,MAAM;AAAA,QACd,cAAc,MAAM;AAAA,QACpB,YAAY,MAAM;AAAA,QAClB,cAAc,MAAM,gBAAgB;AAAA,QACpC,UAAU,iBAAiB,MAAM,QAAQ;AAAA,MAC3C,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,QAAgB,MAAwD;AACjF,WAAO,KAAK,SAAS,KAAK,QAAQ,IAAI;AAAA,EACxC;AAAA;AAAA,EAGA,MAAM,YACJ,QACA,SACA,OACgC;AAChC,WAAO,KAAK,SAAS,YAAY,QAAQ,SAAS,KAAK;AAAA,EACzD;AAAA,EAEA,MAAM,QAAyB;AAC7B,UAAM,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,gBAAgB,KAAK,KAAK,KAAK,GAAI;AAC7E,WAAO,KAAK,SAAS,eAAe,MAAM;AAAA,EAC5C;AACF;AAEA,IAAM,gBAAgB;AAMtB,SAAS,iBACP,UACgC;AAChC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,QAAiC,CAAC;AACxC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,QAAI,cAAc,KAAK,GAAG,EAAG;AAC7B,QAAI,UAAU,OAAW;AACzB,UAAM,GAAG,IAAI;AAAA,EACf;AACA,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,QAAQ;AACjD;AAGO,IAAM,uBAAyC;AAAA,EACpD,MAAM,SAAS;AAAA,EAEf;AACF;;;ACxFO,SAAS,iBAAiB,MAAsB;AAErD,QAAM,OAAO,KAAK,MAAM,OAAO,EAAE,IAAI,KAAK;AAE1C,QAAM,UAAU,KAEb,QAAQ,0BAA0B,EAAE,EACpC,QAAQ,cAAc,GAAG,EACzB,KAAK;AACR,MAAI,CAAC,WAAW,YAAY,OAAO,YAAY,KAAM,QAAO;AAC5D,SAAO,QAAQ,MAAM,GAAG,GAAG;AAC7B;;;AC8BO,IAAM,cAAN,MAAkB;AAAA,EACvB,YACmB,OACA,SACA,WACA,WAA6B,sBAC9C;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAKnB,MAAM,OAAO,QAAgB,OAA6C;AACxE,UAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAI,CAAC,KAAM,OAAM,IAAI,gBAAgB,mBAAmB;AAExD,QAAI,WAAW,MAAM;AACrB,QAAI,MAAM,eAAe;AACvB,YAAM,WAAW,MAAM,KAAK,IAAI,QAAQ,MAAM,aAAa;AAC3D,iBAAW,SAAS;AAAA,IACtB,WAAW,aAAa,MAAM;AAC5B,YAAM,KAAK,aAAa,QAAQ,QAAQ;AAAA,IAC1C;AAEA,UAAM,WAAW,iBAAiB,IAAI;AACtC,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAE1C,UAAM,UAA2B;AAAA,MAC/B,YAAY,MAAM;AAAA,MAClB,QAAQ,MAAM;AAAA,IAChB;AAGA,UAAM,EAAE,QAAQ,QAAQ,KAAK,IAAI,MAAM,OAAO;AAAA,MAC5C,EAAE,MAAM,UAAU,MAAM,MAAM,KAAK;AAAA,MACnC;AAAA,IACF;AACA,UAAM,oBAAoB,OAAO,OAAO,SAAS;AAEjD,QAAI,MAAM,eAAe;AACvB,aAAO,KAAK,eAAe,QAAQ,MAAM,eAAe;AAAA,QACtD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,MAAM,KAAK,MAAM,OAAO;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAID,UAAM,UAAU,MAAM,KAAK,MAAM,cAAc;AAAA,MAC7C,QAAQ,OAAO;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,cAAc,MAAM,KAAK,MAAM,OAAO,OAAO,IAAI;AAAA,MACrD,kBAAkB,QAAQ;AAAA,MAC1B,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY,YAAY;AAAA,MACxB,cAAc,YAAY;AAAA,MAC1B,UAAU,EAAE,MAAM,SAAS;AAAA,IAC7B,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,eACZ,QACA,QACA,MAOqB;AACrB,UAAM,WAAW,MAAM,KAAK,IAAI,QAAQ,MAAM;AAE9C,UAAM,UAAU,MAAM,KAAK,MAAM,cAAc;AAAA,MAC7C;AAAA,MACA;AAAA,MACA,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,mBAAmB,KAAK;AAAA,IAC1B,CAAC;AACD,UAAM,QAAQ,MAAM,KAAK,MAAM,cAAc,MAAM;AAEnD,UAAM,UAAU,MAAM,KAAK,MAAM,OAAO,QAAQ;AAAA,MAC9C,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,mBAAmB,KAAK;AAAA,MACxB,kBAAkB,QAAQ;AAAA,MAC1B,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,MACtB,UAAU,EAAE,gBAAgB,SAAS,QAAQ,MAAM,KAAK,KAAK;AAAA,IAC/D,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,MAAM,KAAK,QAAgB,UAAgD;AACzE,QAAI,aAAa,KAAM,OAAM,KAAK,aAAa,QAAQ,QAAQ;AAC/D,WAAO,KAAK,MAAM,aAAa,QAAQ,QAAQ;AAAA,EACjD;AAAA,EAEA,MAAM,MAAM,QAAgB,OAAkB,MAA+C;AAC3F,QAAI,MAAM,YAAY,KAAM,OAAM,KAAK,aAAa,QAAQ,MAAM,QAAQ;AAC1E,UAAM,QAAmB,MAAM,iBAAiB,KAAK,SAAS,QAAQ,KAAK;AAC3E,WAAO,KAAK,MAAM,MAAM,QAAQ,OAAO,IAAI;AAAA,EAC7C;AAAA,EAEA,MAAM,IAAI,QAAgB,IAAiC;AACzD,UAAM,SAAS,MAAM,KAAK,MAAM,SAAS,EAAE;AAC3C,QAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,kBAAkB,MAAM;AACzF,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,UAAU,QAAgB,IAAiC;AAC/D,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,QAAI,OAAO,UAAW,OAAM,IAAI,cAAc,kBAAkB,MAAM;AACtE,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,QAAgB,IAA0C;AAC3E,UAAM,KAAK,IAAI,QAAQ,EAAE;AACzB,WAAO,KAAK,MAAM,aAAa,EAAE;AAAA,EACnC;AAAA;AAAA,EAIA,MAAM,OAAO,QAAgB,IAAY,MAAmC;AAC1E,UAAM,WAAW,MAAM,KAAK,IAAI,QAAQ,EAAE;AAC1C,UAAM,QAAQ,iBAAiB,IAAI;AACnC,QAAI,CAAC,MAAO,OAAM,IAAI,gBAAgB,mBAAmB;AACzD,UAAM,UAAU,MAAM,KAAK,MAAM,OAAO,IAAI,EAAE,MAAM,MAAM,CAAC;AAC3D,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU,EAAE,cAAc,SAAS,KAAK;AAAA,IAC1C,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,QAAgB,IAAY,UAA8C;AACnF,UAAM,WAAW,MAAM,KAAK,IAAI,QAAQ,EAAE;AAC1C,QAAI,aAAa,KAAM,OAAM,KAAK,aAAa,QAAQ,QAAQ;AAC/D,UAAM,UAAU,MAAM,KAAK,MAAM,OAAO,IAAI,EAAE,SAAS,CAAC;AACxD,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,MACtB,UAAU,EAAE,MAAM,SAAS,UAAU,IAAI,SAAS;AAAA,IACpD,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,QAAgB,IAAY,SAAuC;AAClF,UAAM,KAAK,IAAI,QAAQ,EAAE;AACzB,UAAM,UAAU,MAAM,KAAK,MAAM,OAAO,IAAI,EAAE,QAAQ,CAAC;AACvD,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ,UAAU,SAAS;AAAA,MAC3B,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,IACxB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,QAAgB,IAAiC;AAC3D,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,QAAI,OAAO,UAAW,QAAO;AAC7B,UAAM,UAAU,MAAM,KAAK,MAAM,OAAO,IAAI;AAAA,MAC1C,WAAW,oBAAI,KAAK;AAAA,MACpB,cAAc,QAAQ,EAAE;AAAA,IAC1B,CAAC;AACD,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,IACxB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,QAAQ,QAAgB,IAAiC;AAC7D,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AAIxC,QAAI,WAAW,OAAO;AACtB,QAAI,aAAa,MAAM;AACrB,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,QAAQ;AACnD,UAAI,CAAC,UAAU,OAAO,WAAW,UAAU,OAAO,UAAW,YAAW;AAAA,IAC1E;AAEA,UAAM,UAAU,MAAM,KAAK,MAAM,OAAO,IAAI,EAAE,WAAW,MAAM,cAAc,MAAM,SAAS,CAAC;AAC7F,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,IACxB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,QAAgB,IAA2B;AACjE,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAE1C,UAAM,OAAO,OAAO,EAAE,WAAW,OAAO,OAAO,iBAAiB,EAAE,CAAC;AAEnE,UAAM,WAAW,MAAM,KAAK,MAAM,aAAa,EAAE;AACjD,eAAW,WAAW,UAAU;AAC9B,UAAI,QAAQ,sBAAsB,OAAO,kBAAmB;AAC5D,UAAI;AACF,cAAM,OAAO,OAAO,EAAE,WAAW,OAAO,QAAQ,iBAAiB,EAAE,CAAC;AAAA,MACtE,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,KAAK,MAAM,wBAAwB,CAAC,EAAE,CAAC;AAC7C,UAAM,KAAK,MAAM,WAAW,CAAC,EAAE,CAAC;AAChC,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,QAAgB,IAA2B;AACtD,UAAM,KAAK,kBAAkB,QAAQ,EAAE;AAAA,EACzC;AAAA;AAAA,EAIA,MAAM,SAAS,QAAgB,IAAY,UAA2B,CAAC,GAA0B;AAC/F,UAAM,SAAS,MAAM,KAAK,UAAU,QAAQ,EAAE;AAC9C,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,UAAM,OAAO,MAAM,OAAO,SAAS,EAAE,WAAW,OAAO,OAAO,iBAAiB,EAAE,GAAG,OAAO;AAC3F,UAAM,oBAAoB,MAAM,OAAO,gBAAgB,MAAM,OAAO,MAAM;AAC1E,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,OAAO;AAAA,MACrB,UAAU,EAAE,MAAM,OAAO,MAAM,kBAAkB;AAAA,IACnD,CAAC;AACD,WAAO,EAAE,QAAQ,MAAM,kBAAkB;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,MAAM,QAAgB,IAAY,SAAiB,QAAuB;AAC9E,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,MACJ,QACA,KACA,WACA,UAAwC,CAAC,GAMxC;AACD,UAAM,SAA4C,CAAC;AACnD,QAAI,YAAY;AAEhB,QAAI,IAAI,WAAW,EAAG,OAAM,IAAI,gBAAgB,mBAAmB;AAInE,QAAI,cAAc,QAAQ;AACxB,YAAM,SAAS,QAAQ,YAAY;AACnC,UAAI,WAAW,KAAM,OAAM,KAAK,aAAa,QAAQ,MAAM;AAAA,IAC7D;AAEA,eAAW,MAAM,KAAK;AACpB,UAAI;AACF,gBAAQ,WAAW;AAAA,UACjB,KAAK;AACH,kBAAM,KAAK,MAAM,QAAQ,EAAE;AAC3B;AAAA,UACF,KAAK;AACH,kBAAM,KAAK,QAAQ,QAAQ,EAAE;AAC7B;AAAA,UACF,KAAK;AACH,kBAAM,KAAK,kBAAkB,QAAQ,EAAE;AACvC;AAAA,UACF,KAAK;AACH,kBAAM,KAAK,WAAW,QAAQ,IAAI,IAAI;AACtC;AAAA,UACF,KAAK;AACH,kBAAM,KAAK,WAAW,QAAQ,IAAI,KAAK;AACvC;AAAA,UACF,KAAK;AACH,kBAAM,KAAK,KAAK,QAAQ,IAAI,QAAQ,YAAY,IAAI;AACpD;AAAA,QACJ;AACA,qBAAa;AAAA,MACf,SAAS,OAAO;AACd,eAAO,KAAK,EAAE,IAAI,SAAS,iBAAiB,QAAQ,MAAM,UAAU,mBAAmB,CAAC;AAAA,MAC1F;AAAA,IACF;AAEA,WAAO,EAAE,WAAW,IAAI,QAAQ,WAAW,QAAQ,OAAO,QAAQ,OAAO;AAAA,EAC3E;AAAA;AAAA,EAIA,MAAc,aAAa,QAAgB,UAAiC;AAC1E,UAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,QAAQ;AACnD,QAAI,CAAC,UAAU,OAAO,WAAW,QAAQ;AACvC,YAAM,IAAI,cAAc,oBAAoB,QAAQ;AAAA,IACtD;AACA,QAAI,OAAO,UAAW,OAAM,IAAI,cAAc,mCAAmC;AAAA,EACnF;AACF;;;ACtZO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACmB,SACA,OACA,WACA,WAA6B,sBAC9C;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAKnB,MAAM,OACJ,QACA,OACuB;AACvB,UAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAI,CAAC,KAAM,OAAM,IAAI,gBAAgB,qBAAqB;AAC1D,QAAI,MAAM,aAAa,MAAM;AAC3B,YAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,MAAM,QAAQ;AACpD,UAAI,OAAO,UAAW,OAAM,IAAI,cAAc,mCAAmC;AAAA,IACnF;AACA,UAAM,SAAS,MAAM,KAAK,QAAQ,OAAO,EAAE,QAAQ,UAAU,MAAM,UAAU,KAAK,CAAC;AACnF,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,cAAc,OAAO;AAAA,IACvB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,aAAa,QAAgB,UAAkD;AACnF,QAAI,aAAa,KAAM,OAAM,KAAK,IAAI,QAAQ,QAAQ;AACtD,WAAO,KAAK,QAAQ,aAAa,QAAQ,QAAQ;AAAA,EACnD;AAAA,EAEA,MAAM,MAAM,QAAgB,OAAkB,MAAiD;AAC7F,UAAM,QAAmB,MAAM,iBAAiB,KAAK,SAAS,QAAQ,KAAK;AAC3E,WAAO,KAAK,QAAQ,MAAM,QAAQ,OAAO,IAAI;AAAA,EAC/C;AAAA;AAAA,EAGA,MAAM,KAAK,QAAyC;AAClD,UAAM,MAAM,MAAM,KAAK,QAAQ,WAAW,MAAM;AAChD,WAAO,IAAI,OAAO,CAAC,WAAW,OAAO,cAAc,IAAI;AAAA,EACzD;AAAA,EAEA,MAAM,IAAI,QAAgB,IAAmC;AAC3D,UAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,EAAE;AAC7C,QAAI,CAAC,UAAU,OAAO,WAAW,OAAQ,OAAM,IAAI,cAAc,oBAAoB,QAAQ;AAC7F,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,MAAM,OAAO,QAAgB,IAAY,MAAqC;AAC5E,UAAM,WAAW,MAAM,KAAK,IAAI,QAAQ,EAAE;AAC1C,UAAM,QAAQ,iBAAiB,IAAI;AACnC,QAAI,CAAC,MAAO,OAAM,IAAI,gBAAgB,qBAAqB;AAC3D,UAAM,UAAU,MAAM,KAAK,QAAQ,OAAO,IAAI,EAAE,MAAM,MAAM,CAAC;AAC7D,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,UAAU,EAAE,cAAc,SAAS,KAAK;AAAA,IAC1C,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,QAAgB,IAAY,aAAmD;AACxF,UAAM,WAAW,MAAM,KAAK,IAAI,QAAQ,EAAE;AAE1C,QAAI,gBAAgB,GAAI,OAAM,IAAI,gBAAgB,kCAAkC;AAEpF,QAAI,gBAAgB,MAAM;AACxB,YAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,WAAW;AACjD,UAAI,OAAO,UAAW,OAAM,IAAI,cAAc,mCAAmC;AAIjF,UAAI,SAAwB;AAC5B,aAAO,WAAW,MAAM;AACtB,YAAI,WAAW,IAAI;AACjB,gBAAM,IAAI,gBAAgB,iDAAiD;AAAA,QAC7E;AACA,cAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM;AACjD,YAAI,CAAC,UAAU,OAAO,WAAW;AAC/B,gBAAM,IAAI,cAAc,oBAAoB,QAAQ;AACtD,iBAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,KAAK,QAAQ,OAAO,IAAI,EAAE,UAAU,YAAY,CAAC;AACvE,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,MACtB,UAAU,EAAE,MAAM,SAAS,UAAU,IAAI,YAAY;AAAA,IACvD,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,QAAgB,IAAY,SAAyC;AACpF,UAAM,KAAK,IAAI,QAAQ,EAAE;AACzB,UAAM,UAAU,MAAM,KAAK,QAAQ,OAAO,IAAI,EAAE,QAAQ,CAAC;AACzD,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ,UAAU,SAAS;AAAA,MAC3B,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ;AAAA,IACxB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,MAAM,QAAgB,IAA2C;AACrE,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AACxC,QAAI,OAAO,WAAW;AACpB,aAAO,EAAE,iBAAiB,GAAG,eAAe,EAAE;AAAA,IAChD;AAEA,UAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,EAAE;AACpD,UAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,OAAO;AAIzD,UAAM,UAAU,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrF,UAAM,YAAY,oBAAI,KAAK;AAC3B,UAAM,KAAK,QAAQ;AAAA,MACjB,QAAQ,IAAI,CAAC,WAAW,OAAO,EAAE;AAAA,MACjC,EAAE,WAAW,WAAW,cAAc,QAAQ;AAAA,IAChD;AACA,UAAM,KAAK,MAAM,WAAW,SAAS,EAAE,WAAW,WAAW,cAAc,QAAQ,CAAC;AAEpF,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,OAAO;AAAA,MACrB,UAAU,EAAE,SAAS,QAAQ,QAAQ,OAAO,QAAQ,OAAO;AAAA,IAC7D,CAAC;AAED,WAAO,EAAE,iBAAiB,QAAQ,QAAQ,eAAe,QAAQ,OAAO;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,QAAgB,IAA2C;AACvE,UAAM,SAAS,MAAM,KAAK,IAAI,QAAQ,EAAE;AAIxC,QAAI,WAAW,OAAO;AACtB,QAAI,aAAa,MAAM;AACrB,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,QAAQ;AACnD,UAAI,CAAC,UAAU,OAAO,UAAW,YAAW;AAAA,IAC9C;AAEA,UAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAC9E,UAAM,UAAU,OAAO;AAIvB,UAAM,iBAAiB,CAAC,WAAwB,WAC9C,cAAc,SACb,YAAY,OACT,WAAW,UACX,OAAO,cAAc,QAAQ,UAAU,QAAQ,MAAM,OAAO,UAAU,QAAQ;AAEpF,UAAM,oBAAoB,QAAQ;AAAA,MAAO,CAAC,WACxC,eAAe,OAAO,WAAW,OAAO,YAAY;AAAA,IACtD;AACA,UAAM,sBAAsB,IAAI,IAAI,kBAAkB,IAAI,CAAC,WAAW,OAAO,EAAE,CAAC;AAEhF,UAAM,WAAW,MAAM,KAAK,MAAM,WAAW,MAAM;AACnD,UAAM,kBAAkB,SAAS;AAAA,MAC/B,CAAC,SACC,KAAK,aAAa,QAClB,oBAAoB,IAAI,KAAK,QAAQ,KACrC,eAAe,KAAK,WAAW,KAAK,YAAY;AAAA,IACpD;AAEA,UAAM,oBAAoB,gBAAgB,IAAI,CAAC,SAAS,KAAK,EAAE;AAC/D,QAAI,kBAAkB,SAAS,GAAG;AAChC,YAAM,KAAK,QAAQ;AAAA,QACjB,kBAAkB,IAAI,CAAC,WAAW,OAAO,EAAE;AAAA,QAC3C,EAAE,WAAW,MAAM,cAAc,KAAK;AAAA,MACxC;AACA,YAAM,KAAK,QAAQ,OAAO,IAAI,EAAE,SAAS,CAAC;AAAA,IAC5C;AACA,QAAI,kBAAkB,SAAS,GAAG;AAChC,YAAM,KAAK,MAAM,WAAW,mBAAmB,EAAE,WAAW,MAAM,cAAc,KAAK,CAAC;AAAA,IACxF;AAEA,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,OAAO;AAAA,MACrB,UAAU,EAAE,SAAS,kBAAkB,QAAQ,OAAO,gBAAgB,OAAO;AAAA,IAC/E,CAAC;AAED,WAAO,EAAE,iBAAiB,kBAAkB,QAAQ,eAAe,gBAAgB,OAAO;AAAA,EAC5F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,QAAgB,IAA2C;AACjF,UAAM,KAAK,IAAI,QAAQ,EAAE;AAEzB,UAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAC9E,UAAM,YAAY,QAAQ,IAAI,CAAC,WAAW,OAAO,EAAE;AAEnD,UAAM,WAAW,MAAM,KAAK,MAAM,WAAW,MAAM;AACnD,UAAM,WAAW,SAAS;AAAA,MACxB,CAAC,SAAS,KAAK,aAAa,QAAQ,UAAU,SAAS,KAAK,QAAQ;AAAA,IACtE;AAEA,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,UAAM,iBAA2B,CAAC;AAClC,eAAW,QAAQ,UAAU;AAC3B,UAAI;AACF,cAAM,OAAO,OAAO,EAAE,WAAW,OAAO,KAAK,iBAAiB,EAAE,CAAC;AACjE,uBAAe,KAAK,KAAK,EAAE;AAAA,MAC7B,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,KAAK,MAAM,wBAAwB,cAAc;AACvD,YAAM,KAAK,MAAM,WAAW,cAAc;AAAA,IAC5C;AAGA,UAAM,qBAAqB,IAAI;AAAA,MAC7B,SAAS,OAAO,CAAC,SAAS,CAAC,eAAe,SAAS,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ;AAAA,IAC1F;AACA,UAAM,YAAY,UAAU,OAAO,CAAC,aAAa,CAAC,mBAAmB,IAAI,QAAQ,CAAC;AAClF,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,KAAK,QAAQ,WAAW,SAAS;AAAA,IACzC;AAEA,UAAM,KAAK,SAAS,OAAO;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,cAAc,QAAQ,CAAC,GAAG,QAAQ;AAAA,MAClC,UAAU,EAAE,SAAS,UAAU,QAAQ,OAAO,eAAe,OAAO;AAAA,IACtE,CAAC;AAED,WAAO,EAAE,iBAAiB,UAAU,QAAQ,eAAe,eAAe,OAAO;AAAA,EACnF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,QAAgB,IAA2C;AACtE,WAAO,KAAK,kBAAkB,QAAQ,EAAE;AAAA,EAC1C;AAAA;AAAA;AAAA,EAKA,MAAc,eACZ,QACA,QACA,UAAwC,CAAC,GAChB;AACzB,UAAM,MAAM,MAAM,KAAK,QAAQ,WAAW,MAAM;AAChD,UAAM,aAAa,oBAAI,IAAmC;AAC1D,eAAW,UAAU,KAAK;AACxB,YAAM,OAAO,WAAW,IAAI,OAAO,QAAQ,KAAK,CAAC;AACjD,WAAK,KAAK,MAAM;AAChB,iBAAW,IAAI,OAAO,UAAU,IAAI;AAAA,IACtC;AAEA,UAAM,OAAO,IAAI,KAAK,CAAC,WAAW,OAAO,OAAO,MAAM;AACtD,QAAI,CAAC,KAAM,OAAM,IAAI,cAAc,oBAAoB,QAAQ;AAE/D,UAAM,YAA4B,CAAC,IAAI;AACvC,UAAM,QAAkB,CAAC,MAAM;AAC/B,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,UAAU,MAAM,MAAM;AAC5B,iBAAW,SAAS,WAAW,IAAI,OAAO,KAAK,CAAC,GAAG;AACjD,YAAI,CAAC,QAAQ,kBAAkB,MAAM,UAAW;AAChD,kBAAU,KAAK,KAAK;AACpB,cAAM,KAAK,MAAM,EAAE;AAAA,MACrB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,eAAe,QAAgB,SAA4C;AACvF,UAAM,MAAM,IAAI,IAAI,QAAQ,IAAI,CAAC,WAAW,OAAO,EAAE,CAAC;AACtD,UAAM,WAAW,MAAM,KAAK,MAAM,WAAW,MAAM;AACnD,WAAO,SACJ,OAAO,CAAC,SAAS,KAAK,cAAc,QAAQ,KAAK,aAAa,QAAQ,IAAI,IAAI,KAAK,QAAQ,CAAC,EAC5F,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,EAC1B;AACF;;;ACvVA,IAAM,mBAAmB;AAelB,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACmB,OACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,MAAM,OACJ,QACA,UACA,MACA,YAAuB,CAAC,GACD;AACvB,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,CAAC,QAAS,OAAM,IAAI,gBAAgB,gCAAgC;AACxE,QAAI,QAAQ,SAAS,kBAAkB;AACrC,YAAM,IAAI,gBAAgB,gCAAgC,gBAAgB,aAAa;AAAA,IACzF;AAEA,UAAM,EAAE,MAAM,IAAI,iBAAiB,SAAS,SAAS;AAErD,QAAI,MAAM,WAAW,OAAW,OAAM,SAAS;AAE/C,UAAM,YAAY,MAAM,iBAAiB,KAAK,SAAS,QAAQ,KAAK;AAEpE,UAAM,CAAC,UAAU,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC/C,KAAK,MAAM,MAAM,QAAQ,WAAW,IAAI;AAAA,MACxC,KAAK,QAAQ,MAAM,QAAQ,WAAW,IAAI;AAAA,IAC5C,CAAC;AAID,UAAM,QAAQ,SAAS,QAAQ,WAAW;AAC1C,WAAO;AAAA,MACL,OAAO,SAAS;AAAA,MAChB,SAAS,WAAW;AAAA,MACpB,YAAY,eAAe,KAAK,MAAM,KAAK,OAAO,KAAK;AAAA,IACzD;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,UACJ,QACA,UACA,MACoE;AACpE,WAAO,KAAK,OAAO,QAAQ,UAAU,IAAI;AAAA,EAC3C;AACF;;;ACjEA,IAAM,gBAAgB;AAMf,IAAM,eAAN,MAAmB;AAAA,EACxB,YACmB,OACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,MAAM,MAAM,QAAgB,aAA4B,MAA6B;AACnF,UAAM,CAAC,UAAU,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC7C,KAAK,MAAM,YAAY,MAAM;AAAA;AAAA,MAE7B,KAAK,MAAM,WAAW,MAAM;AAAA,IAC9B,CAAC;AAED,UAAM,SAAS,SAAS,OAAO,CAAC,SAAS,KAAK,cAAc,IAAI;AAChE,UAAM,gBAAgB,oBAAI,IAA8C;AAExE,eAAW,QAAQ,QAAQ;AACzB,YAAM,WAAW,aAAa,KAAK,QAAQ;AAC3C,YAAM,SAAS,cAAc,IAAI,QAAQ,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE;AACnE,aAAO,SAAS,KAAK;AACrB,aAAO,SAAS;AAChB,oBAAc,IAAI,UAAU,MAAM;AAAA,IACpC;AAEA,UAAM,SAAS,CAAC,GAAG,cAAc,QAAQ,CAAC,EACvC,IAAI,CAAC,CAAC,UAAU,MAAM,OAAO,EAAE,UAAU,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM,EAAE,EACpF,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAEnC,UAAM,eAAe,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa;AAEvF,WAAO;AAAA,MACL,WAAW,SAAS;AAAA,MACpB,aAAa,SAAS;AAAA,MACtB,YAAY,SAAS;AAAA,MACrB,YAAY,SAAS;AAAA,MACrB,gBAAgB,SAAS;AAAA,MACzB,cAAc,SAAS;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AC3BO,IAAM,eAAN,MAAmB;AAAA,EACxB,YACmB,OACA,SACA,WACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAHgB;AAAA,EACA;AAAA,EACA;AAAA,EAGnB,MAAM,KAAK,QAAgB,MAAmB,QAAmB,CAAC,GAA0B;AAC1F,UAAM,YAAY,MAAM,iBAAiB,KAAK,SAAS,QAAQ;AAAA,MAC7D,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ,CAAC;AAED,UAAM,CAAC,UAAU,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC/C,KAAK,MAAM,MAAM,QAAQ,WAAW,IAAI;AAAA,MACxC,KAAK,QAAQ,MAAM,QAAQ,WAAW,IAAI;AAAA,IAC5C,CAAC;AAED,UAAM,QAAQ,SAAS,QAAQ,WAAW;AAC1C,WAAO;AAAA,MACL,OAAO,SAAS;AAAA,MAChB,SAAS,WAAW;AAAA,MACpB,YAAY,eAAe,KAAK,MAAM,KAAK,OAAO,KAAK;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,QAA2C;AACrD,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,UAAM,WAAW,MAAM,KAAK,MAAM,WAAW,MAAM;AACnD,UAAM,eAAe,SAAS,OAAO,CAAC,SAAS,KAAK,cAAc,IAAI;AAEtE,QAAI,eAAe;AACnB,QAAI,cAAc;AAClB,UAAM,iBAA2B,CAAC;AAElC,eAAW,QAAQ,cAAc;AAC/B,UAAI;AACF,cAAM,OAAO,OAAO,EAAE,WAAW,OAAO,KAAK,iBAAiB,EAAE,CAAC;AACjE,uBAAe,KAAK,KAAK,EAAE;AAC3B,wBAAgB;AAAA,MAClB,QAAQ;AACN,uBAAe;AAAA,MACjB;AAAA,IACF;AAEA,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,KAAK,MAAM,wBAAwB,cAAc;AACvD,YAAM,KAAK,MAAM,WAAW,cAAc;AAAA,IAC5C;AAIA,UAAM,aAAa,MAAM,KAAK,QAAQ,WAAW,MAAM;AACvD,UAAM,iBAAiB,WAAW,OAAO,CAAC,WAAW,OAAO,cAAc,IAAI;AAC9E,UAAM,QAAQ,CAAC,WAAiC;AAC9C,UAAI,QAAQ;AACZ,UAAI,SAAS,OAAO;AACpB,YAAM,OAAO,oBAAI,IAAY;AAC7B,aAAO,WAAW,QAAQ,CAAC,KAAK,IAAI,MAAM,GAAG;AAC3C,aAAK,IAAI,MAAM;AACf,cAAM,SAAS,WAAW,KAAK,CAAC,cAAc,UAAU,OAAO,MAAM;AACrE,YAAI,CAAC,OAAQ;AACb,iBAAS;AACT,iBAAS,OAAO;AAAA,MAClB;AACA,aAAO;AAAA,IACT;AACA,mBAAe,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;AAEjD,QAAI,iBAAiB;AACrB,eAAW,UAAU,gBAAgB;AAEnC,YAAM,gBAAgB,MAAM,KAAK,MAAM;AAAA,QACrC;AAAA,QACA,EAAE,UAAU,OAAO,IAAI,SAAS,KAAK;AAAA,QACrC,EAAE,MAAM,GAAG,OAAO,EAAE;AAAA,MACtB;AACA,UAAI,cAAc,QAAQ,EAAG;AAC7B,YAAM,KAAK,QAAQ,WAAW,CAAC,OAAO,EAAE,CAAC;AACzC,wBAAkB;AAAA,IACpB;AAEA,WAAO,EAAE,cAAc,gBAAgB,YAAY;AAAA,EACrD;AAAA;AAAA,EAGA,MAAM,kBAAkB,QAAgB,IAAiC;AACvE,UAAM,OAAO,MAAM,KAAK,MAAM,SAAS,EAAE;AACzC,QAAI,CAAC,QAAQ,KAAK,WAAW,UAAU,KAAK,cAAc,MAAM;AAC9D,YAAM,IAAI,cAAc,4BAA4B,MAAM;AAAA,IAC5D;AACA,WAAO;AAAA,EACT;AACF;;;ACpGO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YACmB,OACA,WACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,MAAM,MAAM,QAAgB,UAAiC,CAAC,GAAgC;AAC5F,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,MAAM,MAAM,KAAK,MAAM,WAAW,MAAM;AAC9C,UAAM,SAAS,IAAI,OAAO,CAAC,SAAS,KAAK,cAAc,IAAI;AAC3D,UAAM,UAAU,QAAQ,QAAQ,OAAO,MAAM,GAAG,QAAQ,KAAK,IAAI;AAEjE,UAAM,SAAS,MAAM,KAAK,UAAU,MAAM;AAC1C,UAAM,SAA8B,CAAC;AACrC,QAAI,UAAU;AAEd,eAAW,QAAQ,SAAS;AAC1B,YAAM,QAAQ,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,OAAO,MAAM;AACpE,UAAI,MAAO,QAAO,KAAK,KAAK;AAAA,UACvB,YAAW;AAAA,IAClB;AAEA,UAAM,UAAU,OAAO,OAAO,CAAC,UAAU,MAAM,SAAS,SAAS,EAAE;AACnE,UAAM,eAAe,OAAO;AAAA,MAC1B,CAAC,UAAU,MAAM,SAAS,mBAAmB,MAAM,SAAS;AAAA,IAC9D,EAAE;AACF,UAAM,aAAa,OAAO,OAAO,CAAC,UAAU,MAAM,SAAS,YAAY,EAAE;AAEzE,WAAO;AAAA,MACL,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,cAAc,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,KAAK,IAAI,IAAI;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,MACA,MACA,QACmC;AACnC,UAAM,MAAM,EAAE,WAAW,OAAO,KAAK,iBAAiB,EAAE;AAExD,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,OAAO,KAAK,GAAG;AAAA,IAC9B,SAAS,OAAO;AACd,aAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM;AAAA,QACN,QAAQ,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MACnD;AAAA,IACF;AAEA,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,KAAK,MAAM;AAC3B,aAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,MAAM;AAAA,QACN,QAAQ,YAAY,KAAK,IAAI,YAAY,KAAK,IAAI;AAAA,MACpD;AAAA,IACF;AAEA,QAAI,MAAM;AACR,UAAI;AACF,cAAM,OAAO,MAAM,OAAO,SAAS,GAAG;AACtC,cAAM,KAAK,MAAM,OAAO,gBAAgB,MAAM,KAAK,MAAM;AACzD,YAAI,CAAC,IAAI;AACP,iBAAO;AAAA,YACL,QAAQ,KAAK;AAAA,YACb,MAAM,KAAK;AAAA,YACX,MAAM;AAAA,YACN,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,QAAQ,KAAK;AAAA,UACb,MAAM,KAAK;AAAA,UACX,MAAM;AAAA,UACN,QAAQ,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACtHA,IAAM,iBAAmC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACmB,UACA,OACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,MAAM,KAAK,QAAgB,MAA2C;AAGpE,UAAM,SAAS,KAAK,IAAI,KAAK,OAAO,KAAK,QAAQ,GAAG,GAAG;AACvD,UAAM,SAAS,MAAM,KAAK,SAAS,YAAY,QAAQ,gBAAgB,MAAM;AAE7E,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,SAAS,QAAQ;AAC1B,WAAK,IAAI,MAAM,UAAU;AAAA,IAC3B;AAEA,UAAM,UAAU,CAAC,GAAG,IAAI;AACxB,UAAM,UAAU,QAAQ,SAAS,IAAI,MAAM,KAAK,MAAM,UAAU,QAAQ,OAAO,IAAI,CAAC;AACpF,UAAM,OAAO,IAAI,IAAI,QAAQ,IAAI,CAAC,WAAW,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;AAEjE,UAAM,QAAyB,CAAC;AAChC,eAAW,UAAU,SAAS;AAC5B,YAAM,SAAS,KAAK,IAAI,MAAM;AAC9B,UAAI,CAAC,UAAU,OAAO,cAAc,KAAM;AAC1C,YAAM,QAAQ,OAAO,KAAK,CAAC,cAAc,UAAU,eAAe,MAAM;AACxE,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,UACJ,IAAI,OAAO;AAAA,UACX,MAAM,OAAO;AAAA,UACb,MAAM,OAAO;AAAA,UACb,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,SAAS,OAAO;AAAA,UAChB,SAAS;AAAA,UACT,WAAW;AAAA,UACX,cAAc,OAAO;AAAA,UACrB,kBAAkB,OAAO;AAAA,UACzB,WAAW,OAAO,UAAU,YAAY;AAAA,UACxC,WAAW,OAAO,UAAU,YAAY;AAAA,QAC1C;AAAA,QACA,YAAY,MAAM;AAAA,QAClB,cAAc,MAAM,UAAU,YAAY;AAAA,MAC5C,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,KAAK,OAAO,KAAK,KAAK;AACrC,UAAM,YAAY,MAAM,MAAM,OAAO,QAAQ,KAAK,KAAK;AAEvD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,YAAY,eAAe,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM;AAAA,IAChE;AAAA,EACF;AACF;;;AC3EA,SAAS,KAAK,sBAAsB;AACpC,SAAS,qBAAqB;;;ACD9B,SAAS,gBAAgB;AAalB,SAAS,mBAAmB,OAAmC;AACpE,SAAO,iBAAiB;AAC1B;AAGO,SAAS,wBAAwB,OAAyB;AAC/D,SAAO,mBAAmB,KAAK,KAAK,MAAM,iBAAiB;AAC7D;AAOO,SAAS,iBAAiB,OAAgB,iBAAsC;AACrF,MAAI,iBAAiB,YAAa,QAAO;AAEzC,MAAI,mBAAmB,KAAK,GAAG;AAC7B,UAAM,MAAM,MAAM,gBAAgB;AAElC,QAAI,IAAI,WAAW,aAAa,GAAG;AACjC,YAAM,UAAU,OAAO,IAAI,MAAM,cAAc,MAAM,CAAC,KAAK;AAC3D,aAAO,IAAI,iBAAiB,+CAA0C,OAAO,UAAU;AAAA,IACzF;AAEA,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,eAAO,IAAI,gBAAgB,gCAAgC;AAAA,MAC7D,KAAK;AACH,eAAO,IAAI,gBAAgB,kDAAkD;AAAA,MAC/E,KAAK;AACH,eAAO,IAAI,gBAAgB,2BAA2B;AAAA,MACxD,KAAK;AACH,eAAO,IAAI,gBAAgB,4DAAuD;AAAA,MACpF,KAAK;AACH,eAAO,IAAI,gBAAgB,oCAAoC;AAAA,MACjE,KAAK;AACH,eAAO,IAAI,kBAAkB,sDAAsD;AAAA,MACrF,KAAK;AACH,eAAO,IAAI,gBAAgB,+BAA+B;AAAA,MAC5D,KAAK;AACH,eAAO,IAAI,eAAe,2CAA2C;AAAA,MACvE,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,0BAA0B;AAAA,MACvC,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,2BAA2B;AAAA,MACxC,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,0BAA0B;AAAA,MACvC,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI;AAAA,UACT;AAAA,QACF;AAAA,MACF;AACE,eAAO,IAAI,wBAAwB,iBAAiB;AAAA,UAClD,MAAM,MAAM;AAAA,UACZ,eAAe;AAAA,QACjB,CAAC;AAAA,IACL;AAAA,EACF;AAEA,QAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAGpE,MAAI,uDAAuD,KAAK,MAAM,GAAG;AACvE,WAAO,IAAI,wBAAwB,GAAG,eAAe,oBAAoB,KAAK;AAAA,EAChF;AACA,SAAO,IAAI;AAAA,IACT,SAAS,GAAG,eAAe,KAAK,MAAM,KAAK;AAAA,IAC3C;AAAA,EACF;AACF;;;AD1DA,IAAM,gBAAgB;AAEtB,IAAM,wBAAwB;AAavB,IAAM,wBAAN,MAA4B;AAAA,EAIjC,YACmB,aACA,UACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EALX,cAAc,oBAAI,IAA+B;AAAA,EACjD,kBAAkB,oBAAI,IAAoB;AAAA;AAAA,EAQlD,MAAM,WAAW,QAAyC;AACxD,UAAM,WAAW,KAAK,YAAY,IAAI,MAAM;AAE5C,QAAI,UAAU;AACZ,UAAI,SAAS,UAAU,eAAe,KAAK,YAAY,SAAS,MAAM,GAAG;AACvE,eAAO,SAAS;AAAA,MAClB;AACA,UAAI,SAAS,QAAS,QAAO,SAAS;AACtC,UAAI,SAAS,UAAU,iBAAiB;AACtC,cAAM,IAAI,0BAA0B;AAAA,MACtC;AACA,aAAO,KAAK,UAAU,QAAQ,QAAQ;AAAA,IACxC;AAEA,WAAO,KAAK,YAAY,MAAM;AAAA,EAChC;AAAA;AAAA,EAGA,cAAc,QAAkC;AAC9C,UAAM,aAAa,KAAK,YAAY,IAAI,MAAM;AAC9C,QAAI,CAAC,YAAY;AACf,aAAO,EAAE,OAAO,gBAAgB,WAAW,MAAM,aAAa,MAAM,SAAS,KAAK;AAAA,IACpF;AACA,WAAO;AAAA,MACL,OAAO,WAAW;AAAA,MAClB,WAAW,WAAW;AAAA,MACtB,aAAa,WAAW,cAAc,IAAI,KAAK,WAAW,WAAW,IAAI;AAAA,MACzE,SAAS,WAAW;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,QAAgB,OAAO,OAA+B;AACtE,UAAM,aAAa,KAAK,YAAY,IAAI,MAAM;AAE9C,QAAI,CAAC,YAAY;AACf,UAAI;AACF,cAAM,KAAK,WAAW,MAAM;AAAA,MAC9B,SAAS,OAAO;AACd,cAAM,QACJ,iBAAiB,4BAA4B,kBAAkB;AACjE,cAAM,UAAU,KAAK,YAAY,IAAI,MAAM;AAC3C,YAAI,QAAS,SAAQ,QAAQ;AAC7B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,WAAW;AAAA,UACX,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAClD,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,YAAY,IAAI,MAAM;AAE1C,QAAI,CAAC,QAAQ,OAAO,eAAe,KAAK,IAAI,IAAI,OAAO,cAAc,eAAe;AAClF,aAAO;AAAA,QACL,SAAS,OAAO,eAAe;AAAA,QAC/B,WAAW;AAAA,QACX,SAAS,OAAO;AAAA,QAChB,aAAa;AAAA,MACf;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI;AACF,YAAM,OAAO,OAAO,MAAM;AAC1B,aAAO,cAAc,KAAK,IAAI;AAC9B,aAAO,cAAc;AACrB,aAAO,YAAY;AACnB,aAAO,QAAQ;AACf,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW,KAAK,IAAI,IAAI;AAAA,QACxB,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AACd,YAAM,SAAS,iBAAiB,OAAO,8BAA8B;AACrE,aAAO,cAAc,KAAK,IAAI;AAC9B,aAAO,cAAc;AACrB,aAAO,YAAY,OAAO;AAC1B,aAAO,QACL,kBAAkB,6BAA6B,kBAAkB,oBAC7D,kBACA;AACN,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW,KAAK,IAAI,IAAI;AAAA,QACxB,SAAS,OAAO;AAAA,QAChB,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,QAAgB,QAA8B;AACxD,SAAK,YAAY,IAAI,QAAQ;AAAA,MAC3B;AAAA,MACA,OAAO;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,MACX,aAAa,KAAK,IAAI;AAAA,MACtB,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,eAAe,QAA+B;AAClD,UAAM,aAAa,KAAK,YAAY,IAAI,MAAM;AAC9C,QAAI,CAAC,WAAY;AACjB,SAAK,YAAY,OAAO,MAAM;AAC9B,QAAI;AACF,YAAM,WAAW,OAAO,WAAW;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,gBAA+B;AACnC,UAAM,MAAM,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC;AACvC,UAAM,QAAQ,IAAI,IAAI,IAAI,CAAC,WAAW,KAAK,eAAe,MAAM,CAAC,CAAC;AAAA,EACpE;AAAA;AAAA,EAIQ,YAAY,QAAiC;AACnD,QAAI;AACF,aAAO,OAAO,cAAc;AAAA,IAC9B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,QAAyC;AACjE,UAAM,UAAU,MAAM,KAAK,SAAS,IAAI,MAAM;AAC9C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,0BAA0B,mCAAmC;AAAA,IACzE;AAEA,UAAM,aAAgC;AAAA,MACpC,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,MACX,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAEA,UAAM,UAAU,KAAK,UAAU,QAAQ,aAAa,EAAE,KAAK,CAAC,WAAW;AACrE,iBAAW,SAAS;AACpB,iBAAW,QAAQ;AACnB,iBAAW,UAAU;AACrB,iBAAW,YAAY;AACvB,aAAO;AAAA,IACT,CAAC;AACD,eAAW,UAAU;AACrB,SAAK,YAAY,IAAI,QAAQ,UAAU;AAEvC,QAAI;AACF,aAAO,MAAM;AAAA,IACf,SAAS,OAAO;AACd,iBAAW,UAAU;AACrB,YAAM,SAAS,iBAAiB,OAAO,+BAA+B;AACtE,iBAAW,QACT,kBAAkB,6BAA6B,kBAAkB,oBAC7D,kBACA;AACN,iBAAW,YAAY,OAAO;AAC9B,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,QAAgB,YAAwD;AAC9F,UAAM,cAAc,KAAK,gBAAgB,IAAI,MAAM,KAAK;AACxD,QAAI,KAAK,IAAI,IAAI,cAAc,yBAAyB,WAAW,SAAS;AAC1E,aAAO,WAAW;AAAA,IACpB;AACA,SAAK,gBAAgB,IAAI,QAAQ,KAAK,IAAI,CAAC;AAE3C,eAAW,QAAQ;AACnB,UAAM,UAAU,MAAM,KAAK,SAAS,IAAI,MAAM;AAC9C,QAAI,CAAC,SAAS;AACZ,iBAAW,QAAQ;AACnB,YAAM,IAAI,0BAA0B,mCAAmC;AAAA,IACzE;AAEA,UAAM,WAAW,YAAY;AAC3B,UAAI;AACF,cAAM,WAAW,OAAO,WAAW,EAAE,MAAM,MAAM,MAAS;AAAA,MAC5D,QAAQ;AAAA,MAER;AACA,aAAO,KAAK,UAAU,QAAQ,aAAa;AAAA,IAC7C,GAAG,EACA,KAAK,CAAC,WAAW;AAChB,iBAAW,SAAS;AACpB,iBAAW,QAAQ;AACnB,iBAAW,UAAU;AACrB,iBAAW,YAAY;AACvB,aAAO;AAAA,IACT,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,iBAAW,UAAU;AACrB,YAAM,SAAS,iBAAiB,OAAO,iCAAiC;AACxE,iBAAW,QACT,kBAAkB,6BAA6B,kBAAkB,oBAC7D,kBACA;AACN,iBAAW,YAAY,OAAO;AAC9B,YAAM;AAAA,IACR,CAAC;AAEH,eAAW,UAAU;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,UAAU,eAAgD;AACtE,UAAM,SAAS,IAAI;AAAA,MACjB,IAAI,cAAc,aAAa;AAAA,MAC/B,KAAK,YAAY;AAAA,MACjB,KAAK,YAAY;AAAA,MACjB,EAAE,mBAAmB,EAAE;AAAA,IACzB;AACA,QAAI;AACF,YAAM,OAAO,QAAQ;AAGrB,UAAI,CAAC,OAAO,WAAW;AACrB,cAAM,IAAI,wBAAwB,iCAAiC;AAAA,MACrE;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,+BAA+B;AAAA,IAC/D;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,aAAkD;AAClF,SAAO,IAAI,eAAe,IAAI,cAAc,EAAE,GAAG,YAAY,OAAO,YAAY,SAAS;AAAA,IACvF,mBAAmB;AAAA,EACrB,CAAC;AACH;AAMO,SAAS,sBAAsB,QAAsC;AAC1E,QAAM,QACJ,kBAAkB,IAAI,WAAW,kBAAkB,IAAI,kBAAkB,OAAO,QAAQ,CAAC;AAC3F,aAAW,QAAQ,OAAO;AACxB,QAAI,gBAAgB,IAAI,QAAS,QAAO;AAAA,EAC1C;AACA,QAAM,IAAI,wBAAwB,qDAAqD;AACzF;;;AE5TA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,OAAAC,YAA2B;AACpC,OAAO,YAAY;AACnB,SAAS,kBAAkB;AA0B3B,IAAM,qBAAqB,IAAI,OAAO,OAAO;AAUtC,IAAM,0BAAN,MAAyD;AAAA,EAK9D,YACmB,QACjB,SACA;AAFiB;AAGjB,SAAK,OAAO,IAAIC,KAAI,iBAAiB;AAAA,MACnC,WAAW,OAAO,QAAQ,MAAM;AAAA,MAChC,YAAY,OAAO,QAAQ,UAAU;AAAA,IACvC,CAAC;AACD,SAAK,QAAQ,QAAQ,SAAS;AAAA,EAChC;AAAA,EARmB;AAAA,EALV,OAAO;AAAA,EACC;AAAA,EACA;AAAA;AAAA,EAejB,MAAM,IAAI,OAA2B,UAA2B,CAAC,GAA0B;AACzF,UAAM,OAAO,MAAM,KAAK,YAAY,KAAK;AACzC,QAAI,KAAK,aAAa,oBAAoB;AACxC,YAAM,IAAI;AAAA,QACR,kDAAkD,sBAAsB,OAAO,OAAO,KAAK;AAAA,MAC7F;AAAA,IACF;AACA,SAAK,eAAe,QAAQ,MAAM;AAElC,UAAM,QAAQ,KAAK;AACnB,YAAQ,aAAa,EAAE,aAAa,GAAG,OAAO,SAAS,EAAE,CAAC;AAE1D,QAAI;AACF,YAAM,OAAO,IAAI,WAAW,MAAM,MAAM,OAAO,IAAI,IAAI;AACvD,YAAM,UAAU,MAAM,KAAK,OAAO,SAAS,KAAK,MAAM;AAAA,QACpD;AAAA,QACA,eAAe;AAAA,QACf,SAAS;AAAA;AAAA;AAAA,QAGT,kBAAkB,OAAO;AAAA,UACvB,CAAC,aAAqB;AACpB,kBAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC;AACjD,oBAAQ,aAAa;AAAA,cACnB,aAAa,KAAK,MAAM,UAAU,KAAK;AAAA,cACvC;AAAA,cACA,SAAS,KAAK,MAAM,UAAU,GAAG;AAAA,YACnC,CAAC;AAAA,UACH;AAAA,UACA;AAAA,YACE,IAAI,aAAsB;AACxB,qBAAO,QAAQ,QAAQ,YAAY;AAAA,YACrC;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,UAAI,EAAE,mBAAmBA,KAAI,UAAU;AACrC,cAAM,IAAI,kBAAkB,iDAAiD;AAAA,MAC/E;AAEA,cAAQ,aAAa,EAAE,aAAa,OAAO,OAAO,SAAS,IAAI,CAAC;AAChE,aAAO;AAAA,QACL,WAAW,QAAQ,GAAG,SAAS;AAAA,QAC/B,MAAM,MAAM;AAAA,QACZ,MAAM;AAAA,QACN,UAAU,MAAM;AAAA,MAClB;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,wBAAyB,OAAM;AACpD,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,IAAI,KAAgB,UAA2B,CAAC,GAAoB;AACxE,UAAM,UAAU,MAAM,KAAK,eAAe,GAAG;AAC7C,SAAK,eAAe,QAAQ,MAAM;AAElC,UAAM,OAAO,KAAK,aAAa,OAAO;AACtC,YAAQ,aAAa,EAAE,aAAa,GAAG,OAAO,MAAM,SAAS,EAAE,CAAC;AAEhE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,cAAc,SAAS;AAAA,QAC9C,kBAAkB,CAAC,YAA+B,UAA6B;AAC7E,gBAAM,cAAc,OAAO,WAAW,SAAS,CAAC;AAChD,gBAAM,aAAa,OAAO,MAAM,SAAS,CAAC;AAC1C,kBAAQ,aAAa;AAAA,YACnB;AAAA,YACA,OAAO;AAAA,YACP,SAAS,aAAa,IAAI,KAAK,MAAO,cAAc,aAAc,GAAG,IAAI;AAAA,UAC3E,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,0BAA0B;AAAA,IAC1D;AAEA,QAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,YAAM,IAAI,0BAA0B,sCAAsC;AAAA,IAC5E;AACA,UAAM,SAAS,OAAO,KAAK,IAAI;AAC/B,YAAQ,aAAa;AAAA,MACnB,aAAa,OAAO;AAAA,MACpB,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,IACX,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,KAAgB,UAA2B,CAAC,GAAsB;AAGhF,UAAM,OAAO,MAAM,KAAK,IAAI,KAAK,OAAO;AACxC,WAAOC,UAAS,KAAK,CAAC,IAAI,CAAC;AAAA,EAC7B;AAAA;AAAA,EAIA,MAAM,OAAO,KAA+B;AAC1C,QAAI;AACF,YAAM,KAAK,OAAO,eAAe,KAAK,MAAM,CAAC,OAAO,IAAI,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACvF,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAAkC;AAC7C,QAAI;AACF,aAAQ,MAAM,KAAK,KAAK,GAAG,MAAO;AAAA,IACpC,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAA8C;AACvD,UAAM,UAAU,MAAM,KAAK,WAAW,IAAI,SAAS;AACnD,QAAI,CAAC,SAAS,MAAO,QAAO;AAE5B,UAAM,WAAW,QAAQ;AACzB,QAAI,EAAE,oBAAoBD,KAAI,UAAW,QAAO;AAEhD,QAAI;AACJ,eAAW,QAAQ,SAAS,YAAY;AACtC,UAAI,gBAAgBA,KAAI,2BAA2B;AACjD,mBAAW,KAAK;AAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,WAAW,IAAI;AAAA,MACf,MAAM,YAAY;AAAA,MAClB,MAAM,OAAO,SAAS,KAAK,SAAS,CAAC;AAAA,MACrC,UAAU,SAAS;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAsC;AAC1C,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI;AACF,UAAI,CAAC,KAAK,OAAO,WAAW;AAC1B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,WAAW;AAAA,UACX,SAAS;AAAA,UACT,aAAa,KAAK;AAAA,QACpB;AAAA,MACF;AACA,YAAM,SAAS,MAAM,KAAK,OAAO;AAAA,QAC/B,IAAIA,KAAI,SAAS,eAAe,EAAE,SAAS,KAAK,KAAK,CAAC;AAAA,MACxD;AACA,YAAM,QACJ,kBAAkBA,KAAI,SAAS,YAAY,OAAO,MAAM,CAAC,aAAaA,KAAI,UACtE,OAAO,MAAM,CAAC,EAAE,QAChB,KAAK;AACX,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW,KAAK,IAAI,IAAI;AAAA,QACxB,SAAS;AAAA,QACT,aAAa,SAAS;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,SAAS,iBAAiB,OAAO,sCAAsC;AAC7E,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW,KAAK,IAAI,IAAI;AAAA,QACxB,SAAS,OAAO;AAAA,QAChB,aAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIQ,eAAe,QAA4B;AACjD,QAAI,QAAQ,QAAS,OAAM,IAAI,wBAAwB;AACvD,QAAI,CAAC,KAAK,OAAO,WAAW;AAC1B,YAAM,IAAI,wBAAwB,kCAAkC;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,OAA4C;AACpE,QAAI,MAAM,KAAM,QAAO,MAAM;AAC7B,QAAI,CAAC,MAAM,QAAQ;AACjB,YAAM,IAAI,kBAAkB,4CAA4C;AAAA,IAC1E;AACA,UAAM,SAAmB,CAAC;AAC1B,qBAAiB,SAAS,MAAM,QAAQ;AACtC,aAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAmB,CAAC;AAAA,IAC/E;AACA,WAAO,OAAO,OAAO,MAAM;AAAA,EAC7B;AAAA,EAEQ,aAAa,SAAqC;AACxD,UAAM,WAAW,QAAQ;AACzB,QAAI,oBAAoBA,KAAI,SAAU,QAAO,OAAO,SAAS,KAAK,SAAS,CAAC;AAC5E,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eAAe,KAAsC;AACjE,UAAM,UAAU,MAAM,KAAK,WAAW,IAAI,SAAS;AACnD,QAAI,CAAC,SAAS,OAAO;AACnB,YAAM,IAAI,0BAA0B;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,WAAW,IAA8C;AACrE,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;AAC/E,YAAM,QAAQ,SAAS,CAAC;AACxB,aAAO,iBAAiBA,KAAI,UAAU,QAAQ;AAAA,IAChD,SAAS,OAAO;AACd,UAAI,iBAAiB,0BAA2B,OAAM;AACtD,YAAM,iBAAiB,OAAO,wBAAwB;AAAA,IACxD;AAAA,EACF;AACF;;;AC1RA,SAAS,OAAAE,YAA2B;AACpC,SAAS,oBAAoB;AAiBtB,IAAM,oBAAoB;AAajC,IAAM,oBAAoB,IAAI,KAAK;AAS5B,IAAM,4BAAN,MAAgC;AAAA,EAGrC,YACmB,OACA,eACA,aACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAHgB;AAAA,EACA;AAAA,EACA;AAAA,EALX,QAAQ,oBAAI,IAAuB;AAAA;AAAA,EAS3C,MAAM,WAAW,OAA8B;AAC7C,SAAK,kBAAkB;AACvB,UAAM,KAAK,YAAY,KAAK;AAE5B,UAAM,SAAS,kBAAkB,KAAK,WAAW;AACjD,QAAI;AACF,YAAM,OAAO,QAAQ;AACrB,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO;AAAA,QACrC,EAAE,OAAO,KAAK,YAAY,OAAO,SAAS,KAAK,YAAY,QAAQ;AAAA,QACnE;AAAA,MACF;AACA,WAAK,MAAM,IAAI,OAAO,EAAE,OAAO,QAAQ,eAAe,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IAC/E,SAAS,OAAO;AACd,YAAM,KAAK,kBAAkB,MAAM;AACnC,YAAM,iBAAiB,OAAO,+CAA+C;AAAA,IAC/E;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,WAAW,OAAe,MAAyC;AACvE,UAAM,OAAO,KAAK,YAAY,KAAK;AACnC,QAAI;AACF,YAAM,KAAK,OAAO;AAAA,QAChB,IAAIC,KAAI,KAAK,OAAO;AAAA,UAClB,aAAa,KAAK;AAAA,UAClB,eAAe,KAAK;AAAA,UACpB,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AACd,UAAI,wBAAwB,KAAK,GAAG;AAClC,eAAO,EAAE,QAAQ,oBAAoB;AAAA,MACvC;AACA,YAAM,iBAAiB,OAAO,yBAAyB;AAAA,IACzD;AACA,WAAO,KAAK,cAAc,IAAI;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,eAAe,OAAe,UAA6C;AAC/E,UAAM,OAAO,KAAK,YAAY,KAAK;AACnC,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,OAAO,OAAO,IAAIA,KAAI,QAAQ,YAAY,CAAC;AAC3E,YAAM,QAAQ,MAAM,aAAa,cAAc,QAAQ;AACvD,YAAM,KAAK,OAAO,OAAO,IAAIA,KAAI,KAAK,cAAc,EAAE,UAAU,MAAM,CAAC,CAAC;AAAA,IAC1E,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,yBAAyB;AAAA,IACzD;AACA,WAAO,KAAK,cAAc,IAAI;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,YAAY,OAA8B;AAC9C,UAAM,OAAO,KAAK,MAAM,IAAI,KAAK;AACjC,QAAI,MAAM;AACR,WAAK,MAAM,OAAO,KAAK;AACvB,YAAM,KAAK,kBAAkB,KAAK,MAAM;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,QAAwC;AAC1D,UAAM,WAAW,MAAM,KAAK,MAAM,SAAS,sBAAsB,QAAQ,iBAAiB;AAC1F,QAAI,SAAU,QAAO;AAErB,UAAM,SAAS,MAAM,KAAK,cAAc,WAAW,MAAM;AACzD,WAAO,KAAK,uBAAuB,QAAQ,MAAM;AAAA,EACnD;AAAA,EAEA,MAAM,uBAAuB,QAAgB,QAAgD;AAC3F,UAAM,WAAW,MAAM,KAAK,MAAM,SAAS,sBAAsB,QAAQ,iBAAiB;AAC1F,QAAI,SAAU,QAAO;AAErB,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,OAAO;AAAA,QAC1B,IAAIA,KAAI,SAAS,cAAc;AAAA,UAC7B,OAAO;AAAA,UACP,OACE;AAAA,UACF,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,gBAAU,sBAAsB,MAAM;AAAA,IACxC,SAAS,OAAO;AACd,YAAM,iBAAiB,OAAO,8CAA8C;AAAA,IAC9E;AAEA,UAAM,aAAa,QAAQ,YAAY,SAAS;AAChD,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,wBAAwB,kDAAkD;AAAA,IACtF;AAEA,WAAO,KAAK,MAAM,SAAS,OAAO;AAAA,MAChC;AAAA,MACA,UAAU;AAAA,MACV,OAAO,QAAQ,SAAS;AAAA,MACxB,gBAAgB,QAAQ,GAAG,SAAS;AAAA,MACpC,oBAAoB;AAAA,IACtB,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,YAAY,OAA0B;AAC5C,UAAM,OAAO,KAAK,MAAM,IAAI,KAAK;AACjC,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,cAAc,iDAAiD;AAAA,IAC3E;AACA,QAAI,KAAK,IAAI,IAAI,KAAK,YAAY,mBAAmB;AACnD,WAAK,MAAM,OAAO,KAAK;AACvB,WAAK,KAAK,kBAAkB,KAAK,MAAM;AACvC,YAAM,IAAI,gBAAgB,qDAAgD;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,cAAc,MAA4C;AACtE,SAAK,MAAM,OAAO,KAAK,KAAK;AAE5B,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,sBAAgB,KAAK,OAAO,QAAQ,KAAK;AACzC,YAAM,KAAK,MAAM,KAAK,OAAO,MAAM;AACnC,UAAI,EAAE,cAAcA,KAAI,OAAO;AAC7B,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AACA,aAAO,MAAM,KAAK,MAAM,MAAM,mBAAmB;AAAA,QAC/C,gBAAgB,GAAG,GAAG,SAAS;AAAA,QAC/B,UAAU,GAAG,YAAY;AAAA,QACzB,WAAW,GAAG,aAAa;AAAA,QAC3B,UAAU,GAAG,YAAY;AAAA,QACzB,OAAO,GAAG,SAAS,KAAK;AAAA,MAC1B,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,KAAK,kBAAkB,KAAK,MAAM;AACxC,YAAM,iBAAiB,OAAO,4CAA4C;AAAA,IAC5E;AAEA,UAAM,KAAK,MAAM,SAAS,KAAK,KAAK,IAAI,aAAa;AACrD,UAAM,KAAK,uBAAuB,KAAK,IAAI,KAAK,MAAM;AACtD,SAAK,cAAc,YAAY,KAAK,IAAI,KAAK,MAAM;AAEnD,WAAO,EAAE,QAAQ,MAAM,KAAK;AAAA,EAC9B;AAAA,EAEQ,oBAA0B;AAChC,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,OAAO,IAAI,KAAK,KAAK,OAAO;AACtC,UAAI,MAAM,KAAK,YAAY,mBAAmB;AAC5C,aAAK,MAAM,OAAO,KAAK;AACvB,aAAK,KAAK,kBAAkB,KAAK,MAAM;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,QAAuC;AACrE,QAAI;AACF,YAAM,OAAO,WAAW;AAAA,IAC1B,QAAQ;AAAA,IAER;AAAA,EACF;AACF;","names":["date","Readable","Api","Api","Readable","Api","Api"]}
|