import { normalizeRepositoryRelativePath } from "./repository-file-inventory.ts"; export function parseSecretScanIncludedPaths( value: unknown, ): readonly string[] | null { if (value === undefined) return null; if ( !Array.isArray(value) || value.length === 0 || value.some( (entry) => typeof entry !== "string" || entry.length === 0 || entry.trim() !== entry, ) ) { throw new TypeError( "includedPaths must be a non-empty array of repository-relative POSIX paths", ); } const normalized = value.map((entry) => normalizeRepositoryRelativePath(entry as string, "included path"), ); if (new Set(normalized).size !== normalized.length) { throw new TypeError("includedPaths must not contain duplicate paths"); } return Object.freeze(normalized); } export function selectIncludedInventoryFiles( inventoryFiles: readonly string[], includedPaths: readonly string[] | null, ): readonly string[] { if (includedPaths === null) return Object.freeze([...inventoryFiles]); const validatedIncludedPaths = parseSecretScanIncludedPaths(includedPaths); if (validatedIncludedPaths === null) { throw new TypeError("includedPaths unexpectedly omitted"); } for (const includedPath of validatedIncludedPaths) { if ( !inventoryFiles.some( (file) => file === includedPath || file.startsWith(`${includedPath}/`), ) ) { throw new Error( `secret scan included path matches no inventory file: ${includedPath}`, ); } } return Object.freeze( inventoryFiles.filter((file) => validatedIncludedPaths.some( (includedPath) => file === includedPath || file.startsWith(`${includedPath}/`), ), ), ); }