@lacrous/omnicloud 0.2.8 → 0.2.10

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
@@ -1001,6 +1001,8 @@ interface FileRepository {
1001
1001
  findVersionById(versionId: string): Promise<FileVersionRecord | null>;
1002
1002
  countVersions(fileId: string): Promise<number>;
1003
1003
  deleteVersionsByFileIds(fileIds: string[]): Promise<void>;
1004
+ /** Removes one version row. Callers must delete its Telegram object first. */
1005
+ deleteVersion(versionId: string): Promise<void>;
1004
1006
  }
1005
1007
  interface ActivityRepository {
1006
1008
  record(input: {
@@ -1165,6 +1167,21 @@ declare class StorageEngine {
1165
1167
  private withRetry;
1166
1168
  }
1167
1169
 
1170
+ /**
1171
+ * Which historical versions to keep. The current version is never a candidate,
1172
+ * whatever the policy says. The default keeps everything, so no version is ever
1173
+ * removed unless an operator opts in.
1174
+ */
1175
+ type VersionRetentionPolicy = {
1176
+ kind: "KEEP_ALL";
1177
+ } | {
1178
+ kind: "KEEP_LATEST_N";
1179
+ count: number;
1180
+ } | {
1181
+ kind: "KEEP_FOR_DAYS";
1182
+ days: number;
1183
+ };
1184
+
1168
1185
  interface OperationSink {
1169
1186
  info(record: Record<string, unknown>, message: string): void;
1170
1187
  warn(record: Record<string, unknown>, message: string): void;
@@ -1330,6 +1347,13 @@ declare class FileService {
1330
1347
  message: string;
1331
1348
  }[];
1332
1349
  }>;
1350
+ /**
1351
+ * Removes historical versions the policy no longer keeps. For each version the
1352
+ * Telegram message is deleted first, and its row only after that succeeds, the
1353
+ * same order as a permanent delete. A failed remote delete leaves that version
1354
+ * in place. The current version is never removed. Returns how many were removed.
1355
+ */
1356
+ pruneVersions(userId: string, fileId: string, policy: VersionRetentionPolicy, now?: Date): Promise<number>;
1333
1357
  private assertFolder;
1334
1358
  }
1335
1359
 
package/dist/index.d.ts CHANGED
@@ -1001,6 +1001,8 @@ interface FileRepository {
1001
1001
  findVersionById(versionId: string): Promise<FileVersionRecord | null>;
1002
1002
  countVersions(fileId: string): Promise<number>;
1003
1003
  deleteVersionsByFileIds(fileIds: string[]): Promise<void>;
1004
+ /** Removes one version row. Callers must delete its Telegram object first. */
1005
+ deleteVersion(versionId: string): Promise<void>;
1004
1006
  }
1005
1007
  interface ActivityRepository {
1006
1008
  record(input: {
@@ -1165,6 +1167,21 @@ declare class StorageEngine {
1165
1167
  private withRetry;
1166
1168
  }
1167
1169
 
1170
+ /**
1171
+ * Which historical versions to keep. The current version is never a candidate,
1172
+ * whatever the policy says. The default keeps everything, so no version is ever
1173
+ * removed unless an operator opts in.
1174
+ */
1175
+ type VersionRetentionPolicy = {
1176
+ kind: "KEEP_ALL";
1177
+ } | {
1178
+ kind: "KEEP_LATEST_N";
1179
+ count: number;
1180
+ } | {
1181
+ kind: "KEEP_FOR_DAYS";
1182
+ days: number;
1183
+ };
1184
+
1168
1185
  interface OperationSink {
1169
1186
  info(record: Record<string, unknown>, message: string): void;
1170
1187
  warn(record: Record<string, unknown>, message: string): void;
@@ -1330,6 +1347,13 @@ declare class FileService {
1330
1347
  message: string;
1331
1348
  }[];
1332
1349
  }>;
1350
+ /**
1351
+ * Removes historical versions the policy no longer keeps. For each version the
1352
+ * Telegram message is deleted first, and its row only after that succeeds, the
1353
+ * same order as a permanent delete. A failed remote delete leaves that version
1354
+ * in place. The current version is never removed. Returns how many were removed.
1355
+ */
1356
+ pruneVersions(userId: string, fileId: string, policy: VersionRetentionPolicy, now?: Date): Promise<number>;
1333
1357
  private assertFolder;
1334
1358
  }
1335
1359
 
package/dist/index.js CHANGED
@@ -1294,6 +1294,23 @@ async function resolveItemQuery(folders, userId, input) {
1294
1294
  // ../core/src/services/auth-session-service.ts
1295
1295
  import { createHash as createHash3, randomBytes } from "crypto";
1296
1296
 
1297
+ // ../core/src/services/version-retention.ts
1298
+ function versionsToPrune(versions, currentVersionId, policy, now) {
1299
+ const historical = versions.filter((version) => version.id !== currentVersionId).sort((a, b) => b.versionNumber - a.versionNumber);
1300
+ switch (policy.kind) {
1301
+ case "KEEP_ALL":
1302
+ return [];
1303
+ case "KEEP_LATEST_N": {
1304
+ const keep = Math.max(0, policy.count);
1305
+ return historical.slice(keep);
1306
+ }
1307
+ case "KEEP_FOR_DAYS": {
1308
+ const cutoff = now.getTime() - policy.days * 24 * 60 * 60 * 1e3;
1309
+ return historical.filter((version) => version.createdAt.getTime() < cutoff);
1310
+ }
1311
+ }
1312
+ }
1313
+
1297
1314
  // ../core/src/services/upload-operation.ts
1298
1315
  function isValidOperationId(id) {
1299
1316
  return /^[A-Za-z0-9_-]{8,64}$/.test(id);
@@ -1882,6 +1899,33 @@ var FileService = class {
1882
1899
  };
1883
1900
  }
1884
1901
  // ── internals ────────────────────────────────────────────────────────────
1902
+ /**
1903
+ * Removes historical versions the policy no longer keeps. For each version the
1904
+ * Telegram message is deleted first, and its row only after that succeeds, the
1905
+ * same order as a permanent delete. A failed remote delete leaves that version
1906
+ * in place. The current version is never removed. Returns how many were removed.
1907
+ */
1908
+ async pruneVersions(userId, fileId, policy, now = /* @__PURE__ */ new Date()) {
1909
+ const record = await this.get(userId, fileId);
1910
+ const versions = await this.files.listVersions(fileId);
1911
+ const doomed = versionsToPrune(versions, record.currentVersionId ?? null, policy, now);
1912
+ if (doomed.length === 0) return 0;
1913
+ const engine = await this.engineFor(userId);
1914
+ let removed = 0;
1915
+ for (const version of doomed) {
1916
+ await engine.remove({ messageId: String(version.telegramMessageId) });
1917
+ await this.files.deleteVersion(version.id);
1918
+ removed += 1;
1919
+ }
1920
+ await this.activity.record({
1921
+ userId,
1922
+ action: "delete",
1923
+ resourceType: "file",
1924
+ resourceId: fileId,
1925
+ resourceName: `${record.name} (${removed} old version${removed === 1 ? "" : "s"})`
1926
+ });
1927
+ return removed;
1928
+ }
1885
1929
  async assertFolder(userId, folderId) {
1886
1930
  const folder = await this.folders.findById(folderId);
1887
1931
  if (!folder || folder.userId !== userId) {