/**
 * Folder path validation and normalization for nested folders within a repo.
 *
 * Folder paths are repo-relative POSIX strings (e.g. "clients/acme"). They never
 * start or end with "/", and segments must be filesystem-safe. Empty string is
 * the repo root.
 *
 * `folder_path` is derived from the file's location on disk at read time — it's
 * not stored in frontmatter. That keeps the filesystem as the source of truth
 * and avoids the rename-folder-must-update-N-files problem.
 */
/** Reserved characters disallowed in any folder segment. */
const RESERVED_CHARS = /[<>:"|?*\\]/;
/** Names that are not valid folder segments (filesystem-reserved or special). */
const RESERVED_NAMES = new Set(['.', '..', '.git', '.studiograph', '.gitkeep']);
export class InvalidFolderPathError extends Error {
    constructor(message) {
        super(message);
        this.name = 'InvalidFolderPathError';
    }
}
/**
 * Validate a single folder segment (one level of the path). Throws on:
 * - empty string
 * - leading/trailing whitespace or dots
 * - path separators
 * - reserved chars or names
 */
export function validateFolderSegment(name) {
    if (!name || typeof name !== 'string') {
        throw new InvalidFolderPathError('Folder segment must be a non-empty string');
    }
    if (name !== name.trim()) {
        throw new InvalidFolderPathError(`Folder segment cannot have leading or trailing whitespace: "${name}"`);
    }
    if (name.startsWith('.') || name.endsWith('.')) {
        throw new InvalidFolderPathError(`Folder segment cannot start or end with "." : "${name}"`);
    }
    if (name.includes('/')) {
        throw new InvalidFolderPathError(`Folder segment cannot contain "/" : "${name}"`);
    }
    if (RESERVED_CHARS.test(name)) {
        throw new InvalidFolderPathError(`Folder segment contains reserved characters: "${name}"`);
    }
    if (RESERVED_NAMES.has(name)) {
        throw new InvalidFolderPathError(`Folder segment uses a reserved name: "${name}"`);
    }
}
/**
 * Normalize a folder path: trim slashes, collapse repeats. Returns "" for root.
 * Throws on `..` segments (prevents escaping the repo).
 */
export function normalizeFolderPath(path) {
    if (!path)
        return '';
    // Strip leading/trailing slashes, split, drop empties, validate each.
    const segments = path
        .replace(/^\/+|\/+$/g, '')
        .split('/')
        .filter(s => s.length > 0);
    for (const segment of segments) {
        validateFolderSegment(segment);
    }
    return segments.join('/');
}
/**
 * Validate a full folder path (one or more segments joined by "/"). Returns
 * the normalized form for convenience.
 */
export function validateFolderPath(path) {
    return normalizeFolderPath(path);
}
/**
 * Compute the parent folder path. Returns "" if already at root.
 */
export function parentFolderPath(path) {
    const normalized = normalizeFolderPath(path);
    if (!normalized)
        return '';
    const idx = normalized.lastIndexOf('/');
    return idx === -1 ? '' : normalized.slice(0, idx);
}
/**
 * Derive the folder_path of a file given its absolute path and the repo root.
 * Returns "" if the file lives at the repo root.
 */
export function folderPathFromFile(filePath, repoPath) {
    const normalizedRepo = repoPath.replace(/\/+$/, '');
    if (!filePath.startsWith(normalizedRepo + '/'))
        return '';
    const rel = filePath.slice(normalizedRepo.length + 1);
    const idx = rel.lastIndexOf('/');
    return idx === -1 ? '' : rel.slice(0, idx);
}
//# sourceMappingURL=folder-paths.js.map