@lacrous/omnicloud 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../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":["/**\n * @lacrous/omnicloud — developer SDK for OmniCloud.\n *\n * Contains:\n * - `OmniCloudClient`: HTTP client for an OmniCloud server (browser + Node)\n * - The storage abstraction (`StorageProvider`, `StorageEngine`)\n * - `TelegramStorageProvider` and the Telegram connection service\n * - Domain services (`FileService`, `FolderService`, `SearchService`)\n *\n * The Telegram provider is server-side only (it needs a persistent MTProto\n * connection); `OmniCloudClient` works anywhere.\n */\n\nexport { OmniCloudClient, OmniCloudError } from \"./client\";\nexport type { OmniCloudClientOptions, UploadInput, UploadOptions, UploadProgress } from \"./client\";\n\n// Storage abstraction & services (from the core).\nexport {\n StorageEngine,\n mapProviderError,\n sha256Hex,\n sanitizeFileName,\n FileService,\n FolderService,\n SearchService,\n} from \"@omnicloud/core\";\nexport type {\n DomainError,\n EngineUploadResult,\n FileDownload,\n FileRecord,\n FolderDeletionResult,\n FolderRecord,\n Repos,\n StorageProvider,\n StorageRecord,\n StorageUploadInput,\n StoredObject,\n StoredRef,\n UserRecord,\n} from \"@omnicloud/core\";\n\n// Telegram provider (server-side).\nexport {\n TELEGRAM_PROVIDER,\n TelegramClientManager,\n TelegramConnectionService,\n TelegramStorageProvider,\n mapTelegramError,\n} from \"@omnicloud/telegram\";\nexport type { TelegramCredentials, TelegramStorageOptions } from \"@omnicloud/telegram\";\n\n// Shared DTOs & error codes.\nexport { ERROR_CODES, fileCategory, mimeFromFilename } from \"@omnicloud/shared\";\nexport type {\n ApiErrorBody,\n DeleteFolderResultDTO,\n ErrorCode,\n FileCategory,\n FileDTO,\n FolderDTO,\n SearchResultDTO,\n SessionInfo,\n StorageDTO,\n TelegramVerifyResponse,\n UserDTO,\n} from \"@omnicloud/shared\";\n","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\";\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\";\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\";\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":"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","names":["import_errors","import_telegram","bigInt","import_telegram"]}