import type { FragmentsConfig, TokenConfig } from "./types.js"; const WINDOWS_DRIVE_RE = /^[a-zA-Z]:[\\/]/; export function normalizeConfigPath(path: string): string { return path.trim().replace(/^\.\//, "").replace(/\/+$/, "") || "."; } export function portableRepoPathError(path: string): string | null { const normalized = path.trim(); if (!normalized) return "Path must not be empty"; if (normalized.startsWith("/")) return "Path must be repo-relative, not absolute"; if (WINDOWS_DRIVE_RE.test(normalized)) { return "Path must be repo-relative, not absolute"; } if (normalized.startsWith("~")) return "Path must not use a home directory shortcut"; if (normalized.includes("\\")) return "Path must use POSIX / separators"; const parts = normalized.split("/"); if (parts.some((part) => part === "..")) { return "Path must not contain .. segments"; } if (parts.some((part) => part.length === 0)) { return "Path must not contain empty segments"; } return null; } export function isPortableRepoPath(path: string): boolean { return portableRepoPathError(path) === null; } export function resolveConfiguredAppPath(config: Pick): string { return normalizeConfigPath(config.app?.path ?? "."); } export function topologyBase(config: Pick): "app" | "repo" { return config.topology?.base ?? "app"; } export function tokenIncludesFromConfig(tokens: TokenConfig): string[] { if (tokens.sources?.length) { return tokens.sources.map((source) => source.path); } return tokens.include ?? []; }