import path from 'node:path'; export function normalizeRelPath(value) { return String(value || '') .trim() .replace(/\\/g, '/') .replace(/^\/+|\/+$/g, ''); } export function folderNameOk(name) { return !!name && !name.includes('/') && name !== '.' && name !== '..'; } export function safeJoin(base, rel) { const baseResolved = path.resolve(base); const normalized = path.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, ''); const full = path.resolve(baseResolved, normalized); if (full !== baseResolved && !full.startsWith(baseResolved + path.sep)) { throw new Error('invalid path'); } return full; } export function basenameOf(rel) { const parts = normalizeRelPath(rel).split('/'); return parts[parts.length - 1] || rel; } export function parentOf(rel) { const normalized = normalizeRelPath(rel); const idx = normalized.lastIndexOf('/'); return idx >= 0 ? normalized.slice(0, idx) : ''; } export function joinRel(parent, name) { const p = normalizeRelPath(parent); const n = normalizeRelPath(name); return p ? `${p}/${n}` : n; } export function isReservedName(ctx, name) { const base = basenameOf(name); return ( [ctx.trashDirName, ctx.metaDirName, ctx.sessionDirName].includes(base) || base.startsWith('.drive-') || base.startsWith('.fds-') ); } export function trashRoot(ctx) { return path.join(ctx.uploadDir, ctx.trashDirName); } export function metaFile(ctx) { return path.join(ctx.uploadDir, ctx.metaDirName, 'registry.json'); } export function sessionRoot(ctx) { return path.join(ctx.uploadDir, ctx.sessionDirName); }