export class ProjectLimitsPolicy { constructor() { this.softFileLimit = 1000; this.hardFileLimit = 10000; this.softSizeLimitBytes = 1 * 1024 * 1024; this.hardSizeLimitBytes = 10 * 1024 * 1024; this.ignoredDirectoryNames = new Set(["app-data", "build", "grafana", "__pycache__"]); } summarizeFileList(fileList) { let totalFiles = 0; let totalBytes = 0; for (const file of fileList) { const relPath = (file.webkitRelativePath || file.name || "").replaceAll("\\", "/"); if (this.#isIgnoredPath(relPath)) continue; totalFiles += 1; totalBytes += Number(file.size || 0); } return { totalFiles, totalBytes }; } evaluate(stats) { const softWarnings = []; const hardErrors = []; if (stats.totalFiles > this.hardFileLimit) { hardErrors.push(`Количество файлов ${stats.totalFiles} превышает лимит ${this.hardFileLimit}.`); } else if (stats.totalFiles > this.softFileLimit) { softWarnings.push(`Количество файлов ${stats.totalFiles} больше ${this.softFileLimit}.`); } if (stats.totalBytes > this.hardSizeLimitBytes) { hardErrors.push( `Размер данных ${this.#formatBytes(stats.totalBytes)} превышает лимит ${this.#formatBytes(this.hardSizeLimitBytes)}.` ); } else if (stats.totalBytes > this.softSizeLimitBytes) { softWarnings.push( `Размер данных ${this.#formatBytes(stats.totalBytes)} больше ${this.#formatBytes(this.softSizeLimitBytes)}.` ); } return { softWarnings, hardErrors }; } #isIgnoredPath(path) { const parts = String(path || "") .split("/") .filter(Boolean); return parts.some((segment) => segment.startsWith(".") || this.ignoredDirectoryNames.has(segment)); } #formatBytes(bytes) { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; } }