61 lines · 1.8 KB · TypeScript
RawDownload
/** * Sanitize an arbitrary string into a valid, lowercase git branch fragment. * Strips quotes, collapses separators, limits to 64 chars. */export function sanitizeBranchFragment(raw: string): string {  const normalized = raw    .trim()    .toLowerCase()    .replace(/['"`]/g, "")    .replace(/^[./\s_-]+|[./\s_-]+$/g, "");   const branchFragment = normalized    .replace(/[^a-z0-9/_-]+/g, "-")    .replace(/\/+/g, "/")    .replace(/-+/g, "-")    .replace(/^[./_-]+|[./_-]+$/g, "")    .slice(0, 64)    .replace(/[./_-]+$/g, "");   return branchFragment.length > 0 ? branchFragment : "update";} /** * Sanitize a string into a `feature/…` branch name. * Preserves an existing `feature/` prefix or slash-separated namespace. */export function sanitizeFeatureBranchName(raw: string): string {  const sanitized = sanitizeBranchFragment(raw);  if (sanitized.includes("/")) {    return sanitized.startsWith("feature/") ? sanitized : `feature/${sanitized}`;  }  return `feature/${sanitized}`;} const AUTO_FEATURE_BRANCH_FALLBACK = "feature/update"; /** * Resolve a unique `feature/…` branch name that doesn't collide with * any existing branch. Appends a numeric suffix when needed. */export function resolveAutoFeatureBranchName(  existingBranchNames: readonly string[],  preferredBranch?: string,): string {  const preferred = preferredBranch?.trim();  const resolvedBase = sanitizeFeatureBranchName(    preferred && preferred.length > 0 ? preferred : AUTO_FEATURE_BRANCH_FALLBACK,  );  const existingNames = new Set(existingBranchNames.map((branch) => branch.toLowerCase()));   if (!existingNames.has(resolvedBase)) {    return resolvedBase;  }   let suffix = 2;  while (existingNames.has(`${resolvedBase}-${suffix}`)) {    suffix += 1;  }   return `${resolvedBase}-${suffix}`;}