export function isRelativePathOrDescendant(relative: string, candidate: string): boolean { return relative === candidate || relative.startsWith(`${candidate}/`); } function pathContainsSegmentOrDescendant(relative: string, segment: string): boolean { return relative === segment || relative.startsWith(`${segment}/`) || relative.endsWith(`/${segment}`) || relative.includes(`/${segment}/`); } export function excludePatternMatches(relative: string, pattern: string): boolean { if (pattern.startsWith("*/") && pattern.endsWith("/*")) { return pathContainsSegmentOrDescendant(relative, pattern.slice(2, -2)); } if (pattern.startsWith("*/")) { return pathContainsSegmentOrDescendant(relative, pattern.slice(2)); } if (pattern.endsWith("/*")) { const base = pattern.slice(0, -2); return relative.startsWith(`${base}/`); } return isRelativePathOrDescendant(relative, pattern); } export function shouldExcludePath(relative: string, exclude: readonly string[]): boolean { return exclude.some((entry) => excludePatternMatches(relative, entry)); }