88 lines
3.4 KiB
JavaScript
88 lines
3.4 KiB
JavaScript
|
|
/* eslint-disable no-undef */
|
||
|
|
function createPathValidationApi(ctx) {
|
||
|
|
with (ctx) {
|
||
|
|
function getDefaultShell() {
|
||
|
|
return getDefaultLocalShell();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validate a path - check if it exists and whether it's a file or directory
|
||
|
|
* @param {object} event - IPC event
|
||
|
|
* @param {object} payload - Contains { path: string, type?: 'file' | 'directory' | 'any' }
|
||
|
|
* @returns {{ exists: boolean, isFile: boolean, isDirectory: boolean, isExecutable: boolean }}
|
||
|
|
*
|
||
|
|
* `isExecutable` mirrors isExecutableFile(): POSIX requires the file mode
|
||
|
|
* to have an execute bit; Win32 treats any regular file as executable
|
||
|
|
* (NTFS lacks POSIX bits — extension/PATHEXT decides at spawn time).
|
||
|
|
* Existing callers ignore the new field; consumers that need exec
|
||
|
|
* semantics (e.g. Mosh client path) read it explicitly.
|
||
|
|
*/
|
||
|
|
function statIsExecutable(stat) {
|
||
|
|
if (!stat || !stat.isFile()) return false;
|
||
|
|
if (process.platform === "win32") return true;
|
||
|
|
return (stat.mode & 0o111) !== 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function validatePath(event, payload) {
|
||
|
|
const targetPath = payload?.path;
|
||
|
|
const type = payload?.type || 'any';
|
||
|
|
if (!targetPath) {
|
||
|
|
return { exists: false, isFile: false, isDirectory: false, isExecutable: false };
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Resolve path (handle ~, etc.)
|
||
|
|
let resolvedPath = expandHomePath(targetPath);
|
||
|
|
resolvedPath = path.resolve(resolvedPath);
|
||
|
|
|
||
|
|
if (fs.existsSync(resolvedPath)) {
|
||
|
|
const stat = fs.statSync(resolvedPath);
|
||
|
|
return {
|
||
|
|
exists: true,
|
||
|
|
isFile: stat.isFile(),
|
||
|
|
isDirectory: stat.isDirectory(),
|
||
|
|
isExecutable: statIsExecutable(stat),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// If type is 'file' and path doesn't exist, try to resolve via PATH (for executables like cmd.exe, powershell.exe)
|
||
|
|
if (type === 'file') {
|
||
|
|
const resolvedExecutable = findExecutable(targetPath);
|
||
|
|
// findExecutable returns the original name if not found, so check if it actually resolves to a real path
|
||
|
|
if (resolvedExecutable !== targetPath && fs.existsSync(resolvedExecutable)) {
|
||
|
|
const stat = fs.statSync(resolvedExecutable);
|
||
|
|
return {
|
||
|
|
exists: true,
|
||
|
|
isFile: stat.isFile(),
|
||
|
|
isDirectory: stat.isDirectory(),
|
||
|
|
isExecutable: statIsExecutable(stat),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
// Also try with .exe extension on Windows if not already present
|
||
|
|
if (process.platform === 'win32' && !targetPath.toLowerCase().endsWith('.exe')) {
|
||
|
|
const withExe = findExecutable(targetPath + '.exe');
|
||
|
|
if (withExe !== targetPath + '.exe' && fs.existsSync(withExe)) {
|
||
|
|
const stat = fs.statSync(withExe);
|
||
|
|
return {
|
||
|
|
exists: true,
|
||
|
|
isFile: stat.isFile(),
|
||
|
|
isDirectory: stat.isDirectory(),
|
||
|
|
isExecutable: statIsExecutable(stat),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return { exists: false, isFile: false, isDirectory: false, isExecutable: false };
|
||
|
|
} catch (err) {
|
||
|
|
console.warn(`[Terminal] Error validating path "${targetPath}":`, err.message);
|
||
|
|
return { exists: false, isFile: false, isDirectory: false, isExecutable: false };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return { getDefaultShell, validatePath };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { createPathValidationApi };
|