test / dist /utils /fileSystem.js
bipulsardar's picture
Tiger
7ed99d9
Raw History Blame Contribute Delete
8.08 kB
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DATA_ROOT = void 0;
exports.ensureDataRootExists = ensureDataRootExists;
exports.formatBytes = formatBytes;
exports.safeResolvePath = safeResolvePath;
exports.getBreadcrumbs = getBreadcrumbs;
exports.listDirectory = listDirectory;
exports.getFileDetails = getFileDetails;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const mime_types_1 = __importDefault(require("mime-types"));
// Storage root directory: defaults to /data on HuggingFace Space
// Can be customized via DATA_DIR env var (e.g. for local testing)
const DEFAULT_DATA_DIR = process.platform === 'win32' ? path_1.default.resolve(process.cwd(), 'data_local') : '/data';
exports.DATA_ROOT = path_1.default.resolve(process.env.DATA_DIR || DEFAULT_DATA_DIR);
// Ensure root storage directory exists in local/dev environment
function ensureDataRootExists() {
if (!fs_1.default.existsSync(exports.DATA_ROOT)) {
try {
fs_1.default.mkdirSync(exports.DATA_ROOT, { recursive: true });
console.log(`[Storage] Created storage directory at: ${exports.DATA_ROOT}`);
}
catch (err) {
console.error(`[Storage Error] Could not create storage root ${exports.DATA_ROOT}:`, err);
}
}
return exports.DATA_ROOT;
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0)
return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function safeResolvePath(subPath = '') {
ensureDataRootExists();
// Normalize and remove leading slashes
const cleanSubPath = path_1.default.normalize(subPath).replace(/^(\.\.[\/\\])+/, '').replace(/^[/\\]+/, '');
const fullPath = path_1.default.resolve(exports.DATA_ROOT, cleanSubPath);
// Security check: path traversal prevention
if (!fullPath.startsWith(exports.DATA_ROOT)) {
throw new Error('Access denied: Invalid path traversal target.');
}
const relativePath = path_1.default.relative(exports.DATA_ROOT, fullPath).replace(/\\/g, '/');
return { fullPath, relativePath };
}
function getBreadcrumbs(relativePath) {
const parts = relativePath.split('/').filter(Boolean);
const crumbs = [{ name: 'data', path: '' }];
let accumulated = '';
for (const part of parts) {
accumulated = accumulated ? `${accumulated}/${part}` : part;
crumbs.push({ name: part, path: accumulated });
}
return crumbs;
}
function getCategory(isDirectory, ext, mimeType) {
if (isDirectory)
return 'folder';
ext = ext.toLowerCase();
if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico'].includes(ext))
return 'image';
if (['txt', 'md', 'json', 'js', 'ts', 'html', 'css', 'py', 'sh', 'yaml', 'yml', 'xml', 'c', 'cpp', 'rs', 'go', 'java'].includes(ext))
return 'code';
if (['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(ext))
return 'document';
if (['mp3', 'wav', 'ogg', 'flac', 'm4a'].includes(ext))
return 'audio';
if (['mp4', 'webm', 'mkv', 'avi', 'mov'].includes(ext))
return 'video';
if (['zip', 'tar', 'gz', 'bz2', '7z', 'rar'].includes(ext))
return 'archive';
if (['csv', 'parquet', 'arrow', 'feather', 'h5', 'hdf5', 'npy', 'npz', 'pt', 'pth', 'bin', 'onnx', 'safetensors'].includes(ext))
return 'data';
return 'file';
}
function listDirectory(subPath = '') {
const { fullPath, relativePath } = safeResolvePath(subPath);
if (!fs_1.default.existsSync(fullPath)) {
throw new Error(`Directory does not exist: /data/${relativePath}`);
}
const stat = fs_1.default.statSync(fullPath);
if (!stat.isDirectory()) {
throw new Error(`Path /data/${relativePath} is a file, not a directory.`);
}
const filenames = fs_1.default.readdirSync(fullPath);
const items = [];
let totalFiles = 0;
let totalFolders = 0;
let totalSize = 0;
for (const name of filenames) {
try {
const itemFullPath = path_1.default.join(fullPath, name);
const itemStat = fs_1.default.statSync(itemFullPath);
const isDir = itemStat.isDirectory();
const ext = isDir ? '' : path_1.default.extname(name).slice(1);
const mimeType = isDir ? '' : (mime_types_1.default.lookup(name) || 'application/octet-stream');
const itemRelPath = relativePath ? `${relativePath}/${name}` : name;
const size = isDir ? 0 : itemStat.size;
if (isDir) {
totalFolders++;
}
else {
totalFiles++;
totalSize += size;
}
items.push({
name,
relativePath: itemRelPath,
isDirectory: isDir,
size,
formattedSize: isDir ? '--' : formatBytes(size),
modifiedAt: itemStat.mtime,
extension: ext,
mimeType,
category: getCategory(isDir, ext, mimeType),
});
}
catch (e) {
console.warn(`Could not stat item ${name} in ${fullPath}`, e);
}
}
// Sort folders first, then files alphabetically
items.sort((a, b) => {
if (a.isDirectory && !b.isDirectory)
return -1;
if (!a.isDirectory && b.isDirectory)
return 1;
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base', numeric: true });
});
return {
items,
relativePath,
breadcrumbs: getBreadcrumbs(relativePath),
totalFiles,
totalFolders,
totalSize,
};
}
function getFileDetails(subPath) {
const { fullPath, relativePath } = safeResolvePath(subPath);
if (!fs_1.default.existsSync(fullPath)) {
throw new Error(`File does not exist: /data/${relativePath}`);
}
const stat = fs_1.default.statSync(fullPath);
if (stat.isDirectory()) {
throw new Error(`Path is a directory, not a file.`);
}
const name = path_1.default.basename(fullPath);
const ext = path_1.default.extname(name).slice(1).toLowerCase();
const mimeType = mime_types_1.default.lookup(name) || 'application/octet-stream';
const category = getCategory(false, ext, mimeType);
let textContent = null;
const MAX_PREVIEW_SIZE = 1024 * 1024; // 1 MB limit for text preview
if (category === 'code' || ['csv', 'txt', 'json', 'md', 'log', 'yaml', 'yml'].includes(ext)) {
if (stat.size <= MAX_PREVIEW_SIZE) {
try {
textContent = fs_1.default.readFileSync(fullPath, 'utf-8');
}
catch (err) {
textContent = `[Unable to read text content: ${err}]`;
}
}
else {
// Read first 50KB for large files
try {
const fd = fs_1.default.openSync(fullPath, 'r');
const buffer = Buffer.alloc(50 * 1024);
const bytesRead = fs_1.default.readSync(fd, buffer, 0, buffer.length, 0);
fs_1.default.closeSync(fd);
textContent = buffer.toString('utf-8', 0, bytesRead) + `\n\n... [Preview truncated. Total size: ${formatBytes(stat.size)}]`;
}
catch (err) {
textContent = `[Error reading partial preview]`;
}
}
}
return {
name,
relativePath,
fullPath,
size: stat.size,
formattedSize: formatBytes(stat.size),
modifiedAt: stat.mtime,
extension: ext,
mimeType,
category,
textContent,
breadcrumbs: getBreadcrumbs(relativePath),
};
}