@lacrous/omnicloud 0.1.0 → 0.2.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.
package/dist/index.d.cts CHANGED
@@ -1,14 +1,12 @@
1
- import { TelegramClient } from 'telegram';
2
-
3
- /** Derives a MIME type from the filename extension. Never trusts client input. */
4
- declare function mimeFromFilename(filename: string): string;
5
- type FileCategory = "image" | "video" | "audio" | "pdf" | "archive" | "document" | "text" | "other";
6
- /** Coarse category used for icon selection in the UI. */
7
- declare function fileCategory(mimeType: string): FileCategory;
1
+ import { ListQuery } from './query';
2
+ export { ItemStatus, ListQuery, SortField, SortOrder } from './query';
3
+ export { ActivityEventRecord, ActivityRecorder, ActivityService, DomainError, EngineResolver, EngineRetryPolicy, EngineUploadResult, FileDownload, FileRecord, FileService, FileTypeBucket, FileVersionRecord, FolderMutationResult, FolderRecord, FolderService, IntegrityCheckOptions, IntegrityService, ItemQuery, PageRequest, Paged, RecentService, Repos, SearchService, StatsService, StorageEngine, StorageHealth, StorageProvider, StorageRecord, StorageStats, StorageUploadInput, StoredObject, StoredRef, TransferControl, TransferProgress, TrashService, UserRecord, mapProviderError, noopActivityRecorder, sanitizeFileName, sha256Hex } from './errors';
4
+ export { ConnectionStatus, TELEGRAM_PROVIDER, TelegramClientManager, TelegramConnectionService, TelegramCredentials, TelegramStorageOptions, TelegramStorageProvider, mapTelegramError } from './client';
5
+ export { FileCategory, fileCategory, isFileCategory, mimeFromFilename, mimeMatchersForType, paginationMeta, parseSearchQuery, parseSize } from './mime';
8
6
 
9
7
  /**
10
- * Data transfer objects and constants shared between the API, the web app,
11
- * and the @lacrous/omnicloud SDK.
8
+ * Data transfer objects, constants and error codes shared between the API,
9
+ * the web app, and the @lacrous/omnicloud SDK.
12
10
  *
13
11
  * Telegram identifiers that may exceed JavaScript's safe integer range are
14
12
  * transported as strings across the API boundary.
@@ -25,11 +23,31 @@ interface StorageDTO {
25
23
  title: string;
26
24
  createdAt: string;
27
25
  }
26
+ /** Connection state of the underlying storage backend. */
27
+ type ConnectionState$1 = "DISCONNECTED" | "CONNECTING" | "CONNECTED" | "RECONNECTING" | "ERROR" | "AUTH_REQUIRED";
28
+ type HealthStatus = "healthy" | "degraded" | "unavailable" | "unknown";
29
+ interface StorageHealthDTO {
30
+ provider: string;
31
+ state: ConnectionState$1;
32
+ status: HealthStatus;
33
+ latencyMs: number | null;
34
+ message: string | null;
35
+ channelTitle: string | null;
36
+ checkedAt: string;
37
+ }
28
38
  /** Returned by GET /api/auth/me. `user` is null when not signed in. */
29
39
  interface SessionInfo {
30
40
  user: UserDTO | null;
31
41
  storage: StorageDTO | null;
42
+ health: StorageHealthDTO | null;
32
43
  }
44
+ /** POST /api/auth/telegram/verify response. */
45
+ type TelegramVerifyResponse = {
46
+ status: "ok";
47
+ user: UserDTO;
48
+ } | {
49
+ status: "password_required";
50
+ };
33
51
  interface FileDTO {
34
52
  id: string;
35
53
  name: string;
@@ -37,6 +55,11 @@ interface FileDTO {
37
55
  mimeType: string;
38
56
  sha256: string;
39
57
  folderId: string | null;
58
+ starred: boolean;
59
+ trashed: boolean;
60
+ deletedAt: string | null;
61
+ versionCount: number;
62
+ currentVersionId: string | null;
40
63
  createdAt: string;
41
64
  updatedAt: string;
42
65
  }
@@ -44,56 +67,193 @@ interface FolderDTO {
44
67
  id: string;
45
68
  name: string;
46
69
  parentId: string | null;
70
+ starred: boolean;
71
+ trashed: boolean;
72
+ deletedAt: string | null;
47
73
  createdAt: string;
48
74
  updatedAt: string;
49
75
  }
76
+ interface FileVersionDTO {
77
+ id: string;
78
+ fileId: string;
79
+ versionNumber: number;
80
+ size: number;
81
+ mimeType: string;
82
+ sha256: string;
83
+ telegramMessageId: number;
84
+ isCurrent: boolean;
85
+ createdAt: string;
86
+ }
50
87
  interface SearchResultDTO {
51
88
  files: FileDTO[];
52
89
  folders: FolderDTO[];
90
+ pagination: PaginationDTO;
53
91
  }
54
- interface DeleteFolderResultDTO {
55
- deletedFolders: number;
92
+ /** Result of a recursive folder deletion / trash operation. */
93
+ interface FolderMutationResultDTO {
94
+ affectedFolders: number;
95
+ affectedFiles: number;
96
+ }
97
+ interface EmptyTrashResultDTO {
56
98
  deletedFiles: number;
99
+ deletedFolders: number;
100
+ failedFiles: number;
101
+ }
102
+
103
+ interface PaginationDTO {
104
+ page: number;
105
+ limit: number;
106
+ total: number;
107
+ hasMore: boolean;
108
+ }
109
+ interface FilesPageDTO {
110
+ files: FileDTO[];
111
+ pagination: PaginationDTO;
112
+ }
113
+ interface FoldersPageDTO {
114
+ folders: FolderDTO[];
115
+ pagination: PaginationDTO;
116
+ }
117
+ interface TrashPageDTO {
118
+ files: FileDTO[];
119
+ folders: FolderDTO[];
120
+ pagination: PaginationDTO;
121
+ }
122
+ interface RecentItemDTO {
123
+ file: FileDTO;
124
+ lastAction: ActivityAction;
125
+ lastActionAt: string;
126
+ }
127
+ interface RecentPageDTO {
128
+ items: RecentItemDTO[];
129
+ pagination: PaginationDTO;
130
+ }
131
+ declare const ACTIVITY_ACTIONS: readonly ["upload", "download", "open", "rename", "move", "star", "unstar", "trash", "restore", "delete", "create_folder", "replace"];
132
+ type ActivityAction = (typeof ACTIVITY_ACTIONS)[number];
133
+ declare const RESOURCE_TYPES: readonly ["file", "folder", "storage"];
134
+ type ResourceType = (typeof RESOURCE_TYPES)[number];
135
+ interface ActivityEventDTO {
136
+ id: string;
137
+ action: ActivityAction;
138
+ resourceType: ResourceType;
139
+ resourceId: string;
140
+ resourceName: string | null;
141
+ metadata: Record<string, unknown> | null;
142
+ createdAt: string;
143
+ }
144
+ interface ActivityPageDTO {
145
+ events: ActivityEventDTO[];
146
+ pagination: PaginationDTO;
147
+ }
148
+ interface StorageTypeBreakdownDTO {
149
+ category: string;
150
+ bytes: number;
151
+ count: number;
152
+ }
153
+ interface StorageStatsDTO {
154
+ fileCount: number;
155
+ folderCount: number;
156
+ totalBytes: number;
157
+ trashBytes: number;
158
+ trashFileCount: number;
159
+ starredCount: number;
160
+ byType: StorageTypeBreakdownDTO[];
161
+ largestFiles: FileDTO[];
162
+ quotaBytes: number | null;
163
+ }
164
+ interface HealthDTO {
165
+ status: HealthStatus;
166
+ database: HealthStatus;
167
+ storage: HealthStatus;
168
+ uptimeSeconds: number;
169
+ version: string;
170
+ }
171
+ interface IntegrityIssueDTO {
172
+ fileId: string;
173
+ name: string;
174
+ kind: "missing" | "size_mismatch" | "unreadable" | "hash_mismatch";
175
+ detail: string | null;
176
+ }
177
+ interface IntegrityReportDTO {
178
+ checkedAt: string;
179
+ filesChecked: number;
180
+ healthy: number;
181
+ missing: number;
182
+ inconsistent: number;
183
+ unreadable: number;
184
+ issues: IntegrityIssueDTO[];
185
+ durationMs: number;
186
+ }
187
+ interface BatchResultDTO {
188
+ requested: number;
189
+ succeeded: number;
190
+ failed: number;
191
+ errors: {
192
+ id: string;
193
+ code: ErrorCode;
194
+ message: string;
195
+ }[];
57
196
  }
58
- /** POST /api/auth/telegram/verify response. */
59
- type TelegramVerifyResponse = {
60
- status: "ok";
61
- user: UserDTO;
62
- } | {
63
- status: "password_required";
64
- };
65
197
  /** Stable machine-readable error codes returned in API error bodies. */
66
- declare const ERROR_CODES$1: {
67
- readonly VALIDATION: "VALIDATION_ERROR";
68
- readonly UNAUTHENTICATED: "UNAUTHENTICATED";
69
- readonly FORBIDDEN: "FORBIDDEN";
198
+ declare const ERROR_CODES: {
199
+ readonly AUTH_REQUIRED: "AUTH_REQUIRED";
200
+ readonly AUTH_INVALID: "AUTH_INVALID";
201
+ readonly PERMISSION_DENIED: "PERMISSION_DENIED";
202
+ readonly INVALID_REQUEST: "INVALID_REQUEST";
70
203
  readonly NOT_FOUND: "NOT_FOUND";
204
+ readonly FILE_NOT_FOUND: "FILE_NOT_FOUND";
205
+ readonly FOLDER_NOT_FOUND: "FOLDER_NOT_FOUND";
71
206
  readonly CONFLICT: "CONFLICT";
72
207
  readonly PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE";
73
208
  readonly RATE_LIMITED: "RATE_LIMITED";
74
209
  readonly STORAGE_NOT_INITIALIZED: "STORAGE_NOT_INITIALIZED";
75
- readonly TELEGRAM_ERROR: "TELEGRAM_ERROR";
210
+ readonly STORAGE_UNAVAILABLE: "STORAGE_UNAVAILABLE";
211
+ readonly TELEGRAM_AUTH_REQUIRED: "TELEGRAM_AUTH_REQUIRED";
212
+ readonly TELEGRAM_CONNECTION_FAILED: "TELEGRAM_CONNECTION_FAILED";
213
+ readonly TELEGRAM_FILE_NOT_FOUND: "TELEGRAM_FILE_NOT_FOUND";
214
+ readonly UPLOAD_FAILED: "UPLOAD_FAILED";
215
+ readonly DOWNLOAD_FAILED: "DOWNLOAD_FAILED";
216
+ readonly INTEGRITY_CHECK_FAILED: "INTEGRITY_CHECK_FAILED";
76
217
  readonly INTERNAL: "INTERNAL_ERROR";
77
218
  };
78
- type ErrorCode$1 = (typeof ERROR_CODES$1)[keyof typeof ERROR_CODES$1];
219
+ type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
79
220
  interface ApiErrorBody {
80
221
  error: {
81
- code: ErrorCode$1;
222
+ code: ErrorCode;
82
223
  message: string;
83
224
  details?: unknown;
225
+ requestId?: string;
84
226
  };
85
227
  }
86
228
 
229
+ /**
230
+ * Errors surfaced by the SDK. `code` mirrors the server's stable error codes
231
+ * and `requestId` correlates with server logs.
232
+ */
87
233
  declare class OmniCloudError extends Error {
88
234
  readonly status: number;
89
235
  readonly code: string;
90
- constructor(status: number, code: string, message: string);
236
+ readonly requestId: string | undefined;
237
+ readonly details: unknown;
238
+ constructor(status: number, code: string, message: string, requestId?: string | undefined, details?: unknown);
239
+ }
240
+ interface RetryOptions {
241
+ /** Total attempts including the first. Default 3. */
242
+ attempts?: number;
243
+ /** Base backoff in ms (doubles per retry). Default 300. */
244
+ baseDelayMs?: number;
91
245
  }
92
246
  interface OmniCloudClientOptions {
93
- /** Base URL of an OmniCloud server. Defaults to same-origin ("" is kept as-is). */
247
+ /** Base URL of an OmniCloud server (e.g. "https://cloud.example"). */
94
248
  baseUrl?: string;
95
- /** Custom fetch implementation (e.g. for tests). */
249
+ /** Bearer token, when the deployment uses token auth instead of cookies. */
250
+ token?: string;
251
+ /** Custom fetch implementation (tests, polyfills). */
96
252
  fetchImpl?: typeof fetch;
253
+ /** Retry policy for transient failures. */
254
+ retry?: RetryOptions;
255
+ /** Extra headers sent with every request. */
256
+ headers?: Record<string, string>;
97
257
  }
98
258
  interface UploadProgress {
99
259
  loaded: number;
@@ -103,426 +263,186 @@ interface UploadProgress {
103
263
  interface UploadOptions {
104
264
  folderId?: string | null;
105
265
  onProgress?: (progress: UploadProgress) => void;
266
+ /** Aborts the upload. */
267
+ signal?: AbortSignal;
268
+ /** Number of retry attempts for failed uploads. Default 2. */
269
+ retry?: number;
106
270
  }
107
271
  interface UploadInput {
108
- /** File content: a File/Blob in the browser, or a Buffer/Uint8Array in Node. */
272
+ /** File content: Blob/File in the browser, Buffer/Uint8Array in Node. */
109
273
  data: Blob | Uint8Array;
274
+ /** Filename; also used to derive the MIME type server-side. */
110
275
  name: string;
111
276
  }
277
+ interface DownloadProgress {
278
+ loaded: number;
279
+ total: number | null;
280
+ percent: number | null;
281
+ }
282
+ interface DownloadResult {
283
+ data: Buffer | Uint8Array;
284
+ contentType: string;
285
+ size: number;
286
+ sha256: string | null;
287
+ integrityVerified: boolean | null;
288
+ }
112
289
  /**
113
- * HTTP client for an OmniCloud server.
290
+ * Naming: every endpoint group is a namespaced sub-client so calls read like
114
291
  *
115
- * Works in browsers (cookie sessions, upload progress via XHR) and Node.js
116
- * (fetch fallback, cookie forwarding is the caller's responsibility).
292
+ * cloud.files.list({ folderId })
293
+ * cloud.trash.empty()
294
+ * cloud.storage.stats()
295
+ *
296
+ * Works in browsers (cookie sessions, XHR upload progress) and Node
297
+ * (fetch, Buffer downloads).
117
298
  */
118
299
  declare class OmniCloudClient {
119
- private readonly baseUrl;
120
- private readonly fetchImpl;
300
+ readonly files: FilesApi;
301
+ readonly folders: FoldersApi;
302
+ readonly trash: TrashApi;
303
+ readonly storage: StorageApi;
304
+ readonly search: SearchApi;
305
+ readonly recent: RecentApi;
306
+ readonly activity: ActivityApi;
307
+ readonly auth: AuthApi;
308
+ /** @internal */
309
+ readonly baseUrl: string;
310
+ /** @internal */
311
+ readonly fetchImpl: typeof fetch;
312
+ /** @internal */
313
+ readonly retry: Required<RetryOptions>;
314
+ /** @internal */
315
+ readonly defaultHeaders: Record<string, string>;
121
316
  constructor(options?: OmniCloudClientOptions);
122
- me(): Promise<SessionInfo>;
123
- startTelegramLogin(phone: string): Promise<void>;
124
- verifyTelegramCode(phone: string, code: string): Promise<TelegramVerifyResponse>;
125
- submitTelegramPassword(phone: string, password: string): Promise<{
126
- status: "ok";
127
- user: UserDTO;
128
- }>;
129
- ensureStorage(): Promise<StorageDTO>;
130
- logout(): Promise<void>;
131
- listFolders(parentId: string | null): Promise<FolderDTO[]>;
132
- folderTree(): Promise<FolderDTO[]>;
133
- createFolder(name: string, parentId?: string | null): Promise<FolderDTO>;
134
- renameFolder(id: string, name: string): Promise<FolderDTO>;
135
- moveFolder(id: string, parentId: string | null): Promise<FolderDTO>;
136
- deleteFolder(id: string): Promise<DeleteFolderResultDTO>;
137
- listFiles(folderId: string | null): Promise<FileDTO[]>;
138
- getFile(id: string): Promise<FileDTO>;
139
- renameFile(id: string, name: string): Promise<FileDTO>;
140
- moveFile(id: string, folderId: string | null): Promise<FileDTO>;
141
- deleteFile(id: string): Promise<void>;
142
- /** URL of the download endpoint (usable as href for direct downloads). */
143
- fileDownloadUrl(id: string): string;
317
+ /** Low-level request helper (retries transient failures). */
318
+ request<T>(method: string, path: string, body?: unknown): Promise<T>;
319
+ /** @internal */
320
+ rawRequest<T>(method: string, path: string, body?: unknown): Promise<T>;
321
+ /** @internal */
322
+ withRetry<T>(operation: () => Promise<T>): Promise<T>;
323
+ /** @internal */
324
+ requestBinary(path: string, onProgress?: (p: DownloadProgress) => void): Promise<DownloadResult>;
325
+ }
326
+ declare class FilesApi {
327
+ private readonly client;
328
+ constructor(client: OmniCloudClient);
329
+ /** `folderId: null` = root; omit it to list every active file. */
330
+ list(query?: ListQuery): Promise<FilesPageDTO>;
331
+ get(id: string): Promise<FileDTO>;
332
+ versions(id: string): Promise<FileVersionDTO[]>;
144
333
  /**
145
- * Uploads a file. Uses XMLHttpRequest when available so `onProgress`
146
- * works (browsers); falls back to fetch without progress in Node.
334
+ * Uploads a file with progress and retry support. Uses XMLHttpRequest when
335
+ * available (browsers) so progress and cancellation work; falls back to
336
+ * fetch elsewhere.
147
337
  */
148
- uploadFile(input: UploadInput, options?: UploadOptions): Promise<FileDTO>;
149
- search(query: string): Promise<SearchResultDTO>;
338
+ upload(input: UploadInput, options?: UploadOptions): Promise<FileDTO>;
339
+ private uploadOnce;
150
340
  private uploadViaXhr;
151
- private request;
152
- }
153
-
154
- /** Stable machine-readable error codes returned in API error bodies. */
155
- declare const ERROR_CODES: {
156
- readonly VALIDATION: "VALIDATION_ERROR";
157
- readonly UNAUTHENTICATED: "UNAUTHENTICATED";
158
- readonly FORBIDDEN: "FORBIDDEN";
159
- readonly NOT_FOUND: "NOT_FOUND";
160
- readonly CONFLICT: "CONFLICT";
161
- readonly PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE";
162
- readonly RATE_LIMITED: "RATE_LIMITED";
163
- readonly STORAGE_NOT_INITIALIZED: "STORAGE_NOT_INITIALIZED";
164
- readonly TELEGRAM_ERROR: "TELEGRAM_ERROR";
165
- readonly INTERNAL: "INTERNAL_ERROR";
166
- };
167
- type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
168
-
169
- /**
170
- * Base class for all domain errors. The API maps these to structured HTTP
171
- * responses; everything else becomes a 500.
172
- */
173
- declare class DomainError extends Error {
174
- readonly code: ErrorCode;
175
- readonly status: number;
176
- readonly details?: unknown;
177
- constructor(code: ErrorCode, status: number, message: string, details?: unknown);
178
- }
179
- /** The underlying storage backend (e.g. Telegram) failed. */
180
- declare class StorageProviderError extends DomainError {
181
- constructor(message: string, details?: unknown);
182
- }
183
-
184
- /**
185
- * Domain records. Repository implementations (e.g. the Prisma layer) map
186
- * their persistence models onto these records; the rest of the application
187
- * only ever works with them.
188
- *
189
- * Telegram identifiers are represented as strings/numbers that fit in
190
- * JavaScript's safe integer range; the persistence layer converts as needed.
191
- */
192
- interface UserRecord {
193
- id: string;
194
- telegramUserId: string;
195
- username: string | null;
196
- firstName: string | null;
197
- lastName: string | null;
198
- phone: string | null;
199
- createdAt: Date;
200
- }
201
- interface TelegramSessionRecord {
202
- userId: string;
203
- stringSession: string;
204
- }
205
- interface StorageRecord {
206
- id: string;
207
- userId: string;
208
- provider: string;
209
- title: string;
210
- telegramChatId: string;
211
- telegramAccessHash: string;
212
- createdAt: Date;
213
- }
214
- interface FolderRecord {
215
- id: string;
216
- userId: string;
217
- parentId: string | null;
218
- name: string;
219
- createdAt: Date;
220
- updatedAt: Date;
221
- }
222
- interface FileRecord {
223
- id: string;
224
- userId: string;
225
- folderId: string | null;
226
- name: string;
227
- size: number;
228
- mimeType: string;
229
- sha256: string;
230
- telegramMessageId: number;
231
- createdAt: Date;
232
- updatedAt: Date;
233
- }
234
-
235
- /**
236
- * Repository interfaces. The application depends on these, not on a specific
237
- * database — the Prisma implementation lives in @omnicloud/database, and
238
- * tests use in-memory implementations.
239
- */
240
- interface UserRepository {
241
- findById(id: string): Promise<UserRecord | null>;
242
- findByTelegramId(telegramUserId: string): Promise<UserRecord | null>;
243
- upsertFromTelegram(input: {
244
- telegramUserId: string;
245
- username: string | null;
246
- firstName: string | null;
247
- lastName: string | null;
248
- phone: string | null;
249
- }): Promise<UserRecord>;
250
- }
251
- interface SessionRepository {
252
- get(userId: string): Promise<TelegramSessionRecord | null>;
253
- save(userId: string, stringSession: string): Promise<void>;
254
- delete(userId: string): Promise<void>;
255
- }
256
- interface StorageRepository {
257
- findByUserAndProvider(userId: string, provider: string): Promise<StorageRecord | null>;
258
- create(input: {
259
- userId: string;
260
- provider: string;
261
- title: string;
262
- telegramChatId: string;
263
- telegramAccessHash: string;
264
- }): Promise<StorageRecord>;
265
- }
266
- interface FolderRepository {
267
- create(input: {
268
- userId: string;
269
- parentId: string | null;
270
- name: string;
271
- }): Promise<FolderRecord>;
272
- findById(id: string): Promise<FolderRecord | null>;
273
- /** Every folder belonging to the user (used for trees and cascade deletes). */
274
- listByUser(userId: string): Promise<FolderRecord[]>;
275
- listChildren(userId: string, parentId: string | null): Promise<FolderRecord[]>;
276
- update(id: string, patch: {
277
- name?: string;
278
- parentId?: string | null;
279
- }): Promise<FolderRecord>;
280
- deleteMany(ids: string[]): Promise<void>;
281
- searchByName(userId: string, query: string, limit: number): Promise<FolderRecord[]>;
282
- }
283
- interface FileRepository {
284
- create(input: {
285
- userId: string;
286
- folderId: string | null;
287
- name: string;
288
- size: number;
289
- mimeType: string;
290
- sha256: string;
291
- telegramMessageId: number;
292
- }): Promise<FileRecord>;
293
- findById(id: string): Promise<FileRecord | null>;
294
- listByFolder(userId: string, folderId: string | null): Promise<FileRecord[]>;
295
- listByUser(userId: string): Promise<FileRecord[]>;
296
- update(id: string, patch: {
297
- name?: string;
341
+ /** Uploads a new version of an existing file. */
342
+ replace(id: string, input: UploadInput): Promise<FileDTO>;
343
+ download(id: string, onProgress?: (p: DownloadProgress) => void): Promise<DownloadResult>;
344
+ /** URL suitable for direct download (anchor href). */
345
+ downloadUrl(id: string): string;
346
+ rename(id: string, name: string): Promise<FileDTO>;
347
+ star(id: string, starred?: boolean): Promise<FileDTO>;
348
+ move(id: string, folderId: string | null): Promise<FileDTO>;
349
+ trash(id: string): Promise<FileDTO>;
350
+ restore(id: string): Promise<FileDTO>;
351
+ /** Permanently deletes a file and its Telegram objects. */
352
+ delete(id: string): Promise<void>;
353
+ /** Batch operation: trash | restore | delete | star | unstar | move. */
354
+ batch(operation: "trash" | "restore" | "delete" | "star" | "unstar" | "move", ids: string[], options?: {
298
355
  folderId?: string | null;
299
- }): Promise<FileRecord>;
300
- deleteMany(ids: string[]): Promise<void>;
301
- searchByName(userId: string, query: string, limit: number): Promise<FileRecord[]>;
302
- }
303
- interface Repos {
304
- users: UserRepository;
305
- sessions: SessionRepository;
306
- storages: StorageRepository;
307
- folders: FolderRepository;
308
- files: FileRepository;
356
+ }): Promise<BatchResultDTO>;
309
357
  }
310
-
311
- /**
312
- * Generic storage abstraction. The application must never depend on
313
- * Telegram-specific details — it talks to a StorageProvider.
314
- */
315
- /** Reference to an object inside the backing store (opaque to callers). */
316
- interface StoredRef {
317
- messageId: string;
318
- }
319
- interface StoredObject extends StoredRef {
320
- name: string;
321
- size: number;
322
- mimeType: string;
323
- }
324
- interface StorageUploadInput {
325
- name: string;
326
- mimeType: string;
327
- data: Buffer;
328
- }
329
- interface StorageProvider {
330
- readonly name: string;
331
- /** Uploads the object and returns the stored reference. */
332
- put(input: StorageUploadInput): Promise<StoredObject>;
333
- /** Downloads the object's content. */
334
- get(ref: StoredRef): Promise<Buffer>;
335
- /** Deletes the object. Deleting an already-missing object must not throw. */
336
- delete(ref: StoredRef): Promise<void>;
337
- exists(ref: StoredRef): Promise<boolean>;
338
- stat(ref: StoredRef): Promise<StoredObject | null>;
339
- }
340
-
341
- declare function sha256Hex(data: Uint8Array): string;
342
-
343
- interface EngineUploadResult {
344
- stored: StoredObject;
345
- sha256: string;
346
- size: number;
347
- }
348
- /**
349
- * Sits between the application and the raw StorageProvider and adds the
350
- * cross-cutting guarantees: SHA-256 integrity and consistent error mapping.
351
- *
352
- * The active provider is chosen per user by the host application (each user
353
- * has their own Telegram storage channel).
354
- */
355
- declare class StorageEngine {
356
- private readonly provider;
357
- constructor(provider: StorageProvider);
358
- get providerName(): string;
359
- /** Uploads an object and computes its checksum in one step. */
360
- upload(input: StorageUploadInput): Promise<EngineUploadResult>;
361
- download(ref: StoredRef): Promise<Buffer>;
362
- remove(ref: StoredRef): Promise<void>;
363
- verifyIntegrity(data: Buffer, expectedSha256: string): Promise<boolean>;
364
- }
365
- declare function mapProviderError(message: string, error: unknown): StorageProviderError;
366
-
367
- interface FileDownload {
368
- record: FileRecord;
369
- data: Buffer;
370
- integrityVerified: boolean;
371
- }
372
- type EngineResolver = (userId: string) => Promise<StorageEngine>;
373
- /**
374
- * All file metadata operations. Every method verifies ownership: records
375
- * belonging to other users are reported as missing.
376
- */
377
- declare class FileService {
378
- private readonly files;
379
- private readonly folders;
380
- private readonly engineFor;
381
- constructor(files: FileRepository, folders: FolderRepository, engineFor: EngineResolver);
382
- upload(userId: string, input: {
383
- folderId: string | null;
384
- name: string;
385
- data: Buffer;
386
- }): Promise<FileRecord>;
387
- list(userId: string, folderId: string | null): Promise<FileRecord[]>;
388
- get(userId: string, id: string): Promise<FileRecord>;
389
- rename(userId: string, id: string, name: string): Promise<FileRecord>;
390
- move(userId: string, id: string, folderId: string | null): Promise<FileRecord>;
391
- delete(userId: string, id: string): Promise<void>;
392
- download(userId: string, id: string): Promise<FileDownload>;
393
- }
394
-
395
- interface FolderDeletionResult {
396
- deletedFolders: number;
397
- deletedFiles: number;
358
+ declare class FoldersApi {
359
+ private readonly client;
360
+ constructor(client: OmniCloudClient);
361
+ list(query?: ListQuery & {
362
+ parentId?: string | null;
363
+ }): Promise<FoldersPageDTO>;
364
+ /** The full active folder tree (for move dialogs). */
365
+ tree(): Promise<FolderDTO[]>;
366
+ create(name: string, parentId?: string | null): Promise<FolderDTO>;
367
+ rename(id: string, name: string): Promise<FolderDTO>;
368
+ star(id: string, starred?: boolean): Promise<FolderDTO>;
369
+ move(id: string, parentId: string | null): Promise<FolderDTO>;
370
+ trash(id: string): Promise<FolderMutationResultDTO>;
371
+ restore(id: string): Promise<FolderMutationResultDTO>;
372
+ /** Permanently deletes the folder subtree and its Telegram objects. */
373
+ delete(id: string): Promise<FolderMutationResultDTO>;
374
+ batch(operation: "trash" | "restore" | "delete" | "star" | "unstar" | "move", ids: string[], options?: {
375
+ parentId?: string | null;
376
+ }): Promise<BatchResultDTO>;
398
377
  }
399
- /**
400
- * Folder deletion semantics (v0.1): deleting a folder recursively deletes
401
- * all descendant folders and the files inside them, including the remote
402
- * storage objects. Files directly inside the deleted hierarchy are removed,
403
- * not relocated.
404
- */
405
- declare class FolderService {
406
- private readonly folders;
407
- private readonly files;
408
- private readonly engineFor;
409
- constructor(folders: FolderRepository, files: FileRepository, engineFor: EngineResolver);
410
- create(userId: string, input: {
411
- name: string;
412
- parentId: string | null;
413
- }): Promise<FolderRecord>;
414
- listChildren(userId: string, parentId: string | null): Promise<FolderRecord[]>;
415
- tree(userId: string): Promise<FolderRecord[]>;
416
- get(userId: string, id: string): Promise<FolderRecord>;
417
- rename(userId: string, id: string, name: string): Promise<FolderRecord>;
418
- move(userId: string, id: string, newParentId: string | null): Promise<FolderRecord>;
419
- delete(userId: string, id: string): Promise<FolderDeletionResult>;
378
+ declare class TrashApi {
379
+ private readonly client;
380
+ constructor(client: OmniCloudClient);
381
+ list(query?: ListQuery): Promise<TrashPageDTO>;
382
+ /** Permanently removes everything in the Trash. */
383
+ empty(): Promise<EmptyTrashResultDTO>;
420
384
  }
421
-
422
- /** Metadata-based search over file and folder names (PostgreSQL-backed). */
423
- declare class SearchService {
424
- private readonly files;
425
- private readonly folders;
426
- constructor(files: FileRepository, folders: FolderRepository);
427
- search(userId: string, query: string, limit?: number): Promise<{
428
- files: FileRecord[];
429
- folders: FolderRecord[];
385
+ declare class StorageApi {
386
+ private readonly client;
387
+ constructor(client: OmniCloudClient);
388
+ /** Creates the private storage channel if it does not exist yet. */
389
+ ensure(): Promise<StorageDTO>;
390
+ stats(): Promise<StorageStatsDTO>;
391
+ health(): Promise<{
392
+ health: StorageHealthDTO;
393
+ stats: StorageStatsDTO;
430
394
  }>;
395
+ /** Probes the storage backend; `deep` performs a real Telegram round-trip. */
396
+ check(deep?: boolean): Promise<StorageHealthDTO>;
397
+ /** Read-only integrity scan; nothing is repaired or deleted. */
398
+ integrityCheck(options?: {
399
+ deep?: boolean;
400
+ }): Promise<IntegrityReportDTO>;
401
+ /** Public server health (no authentication required). */
402
+ serverHealth(): Promise<HealthDTO>;
403
+ }
404
+ declare class SearchApi {
405
+ private readonly client;
406
+ constructor(client: OmniCloudClient);
407
+ /**
408
+ * Searches with the v0.2 query language:
409
+ * `type:pdf`, `size:>100MB`, `folder:Projects`, `starred:true`, `after:2026-01-01`
410
+ */
411
+ query(q: string, page?: {
412
+ page?: number;
413
+ limit?: number;
414
+ }): Promise<SearchResultDTO>;
431
415
  }
432
-
433
- /**
434
- * Sanitizes a client-supplied filename into a safe, single path component.
435
- * Returns an empty string when nothing usable remains.
436
- */
437
- declare function sanitizeFileName(name: string): string;
438
-
439
- interface TelegramCredentials {
440
- apiId: number;
441
- apiHash: string;
442
- }
443
- /**
444
- * Keeps one connected TelegramClient per authenticated user. Clients are
445
- * restored from the persisted MTProto session string and cached for the
446
- * lifetime of the process.
447
- */
448
- declare class TelegramClientManager {
449
- private readonly credentials;
450
- private readonly sessions;
451
- private clients;
452
- constructor(credentials: TelegramCredentials, sessions: SessionRepository);
453
- getForUser(userId: string): Promise<TelegramClient>;
454
- /** Registers an already-authenticated client (e.g. from the login flow). */
455
- cacheClient(userId: string, client: TelegramClient): void;
456
- disconnectAll(): Promise<void>;
457
- private connect;
416
+ declare class RecentApi {
417
+ private readonly client;
418
+ constructor(client: OmniCloudClient);
419
+ list(page?: {
420
+ page?: number;
421
+ limit?: number;
422
+ }): Promise<RecentPageDTO>;
458
423
  }
459
-
460
- /**
461
- * Maps a Telegram (GramJS) failure to a structured domain error with an
462
- * appropriate HTTP status and client-facing message.
463
- */
464
- declare function mapTelegramError(error: unknown, fallbackMessage: string): DomainError;
465
-
466
- interface TelegramStorageOptions {
467
- /** Raw channel id of the user's private storage channel. */
468
- chatId: string;
469
- /** Channel access hash required to build the input peer. */
470
- accessHash: string;
424
+ declare class ActivityApi {
425
+ private readonly client;
426
+ constructor(client: OmniCloudClient);
427
+ list(page?: {
428
+ page?: number;
429
+ limit?: number;
430
+ }): Promise<ActivityPageDTO>;
471
431
  }
472
- /**
473
- * StorageProvider implementation backed by the user's private Telegram
474
- * channel. Files are sent as force-downloaded documents (no compression),
475
- * so every upload is a document message in the channel.
476
- */
477
- declare class TelegramStorageProvider implements StorageProvider {
432
+ declare class AuthApi {
478
433
  private readonly client;
479
- readonly name = "telegram";
480
- private readonly peer;
481
- constructor(client: TelegramClient, options: TelegramStorageOptions);
482
- put(input: StorageUploadInput): Promise<StoredObject>;
483
- get(ref: StoredRef): Promise<Buffer>;
484
- delete(ref: StoredRef): Promise<void>;
485
- exists(ref: StoredRef): Promise<boolean>;
486
- stat(ref: StoredRef): Promise<StoredObject | null>;
487
- private getMessage;
434
+ constructor(client: OmniCloudClient);
435
+ me(): Promise<SessionInfo>;
436
+ startTelegramLogin(phone: string): Promise<void>;
437
+ verifyTelegramCode(phone: string, code: string): Promise<TelegramVerifyResponse>;
438
+ submitTelegramPassword(phone: string, password: string): Promise<{
439
+ status: "ok";
440
+ user: UserDTO;
441
+ }>;
442
+ logout(): Promise<void>;
488
443
  }
489
444
 
490
- declare const TELEGRAM_PROVIDER = "telegram";
491
- type VerifyCodeResult = {
492
- status: "ok";
493
- user: UserRecord;
494
- } | {
495
- status: "password_required";
496
- };
497
- /**
498
- * Orchestrates the full Telegram account connection flow:
499
- * phone → code (→ 2FA password) → session persisted → user upserted →
500
- * private storage channel created.
501
- */
502
- declare class TelegramConnectionService {
503
- private readonly repos;
504
- private readonly clientManager;
505
- private readonly credentials;
506
- private flows;
507
- constructor(repos: Repos, clientManager: TelegramClientManager, credentials: TelegramCredentials);
508
- /** Sends the Telegram confirmation code for the phone number. */
509
- startLogin(phone: string): Promise<void>;
510
- /** Verifies the confirmation code. Returns `password_required` for 2FA accounts. */
511
- verifyCode(phone: string, code: string): Promise<VerifyCodeResult>;
512
- /** Completes sign-in for accounts with two-factor authentication enabled. */
513
- verifyPassword(phone: string, password: string): Promise<VerifyCodeResult>;
514
- /** Aborts a pending login flow (disconnects the client). */
515
- cancelLogin(phone: string): Promise<void>;
516
- /**
517
- * Ensures the user has a registered private storage channel, creating it
518
- * on first use. Idempotent.
519
- */
520
- ensureStorage(userId: string): Promise<StorageRecord>;
521
- ensureStorageForClient(userId: string, client: TelegramClient): Promise<StorageRecord>;
522
- private requireFlow;
523
- private finalizeLogin;
524
- private sweepExpiredFlows;
525
- private disconnectQuietly;
526
- }
445
+ /** Connection state of the underlying storage backend. */
446
+ type ConnectionState = "DISCONNECTED" | "CONNECTING" | "CONNECTED" | "RECONNECTING" | "ERROR" | "AUTH_REQUIRED";
527
447
 
528
- export { type ApiErrorBody, type DeleteFolderResultDTO, DomainError, ERROR_CODES$1 as ERROR_CODES, type EngineUploadResult, type ErrorCode$1 as ErrorCode, type FileCategory, type FileDTO, type FileDownload, type FileRecord, FileService, type FolderDTO, type FolderDeletionResult, type FolderRecord, FolderService, OmniCloudClient, type OmniCloudClientOptions, OmniCloudError, type Repos, type SearchResultDTO, SearchService, type SessionInfo, type StorageDTO, StorageEngine, type StorageProvider, type StorageRecord, type StorageUploadInput, type StoredObject, type StoredRef, TELEGRAM_PROVIDER, TelegramClientManager, TelegramConnectionService, type TelegramCredentials, type TelegramStorageOptions, TelegramStorageProvider, type TelegramVerifyResponse, type UploadInput, type UploadOptions, type UploadProgress, type UserDTO, type UserRecord, fileCategory, mapProviderError, mapTelegramError, mimeFromFilename, sanitizeFileName, sha256Hex };
448
+ export { type ActivityEventDTO, type ActivityPageDTO, type ApiErrorBody, type BatchResultDTO, type ConnectionState, type DownloadProgress, type DownloadResult, ERROR_CODES, type EmptyTrashResultDTO, type ErrorCode, type FileDTO, type FileVersionDTO, type FilesPageDTO, type FolderDTO, type FolderMutationResultDTO, type FoldersPageDTO, type HealthDTO, type HealthStatus, type IntegrityIssueDTO, type IntegrityReportDTO, OmniCloudClient, type OmniCloudClientOptions, OmniCloudError, type PaginationDTO, type RecentItemDTO, type RecentPageDTO, type RetryOptions, type SearchResultDTO, type SessionInfo, type StorageDTO, type StorageHealthDTO, type StorageStatsDTO, type StorageTypeBreakdownDTO, type TelegramVerifyResponse, type TrashPageDTO, type UploadInput, type UploadOptions, type UploadProgress, type UserDTO };