n8n
Version:
n8n Workflow Automation Tool
256 lines • 12 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNpmExecErrorWithStdout = isNpmExecErrorWithStdout;
exports.executeNpmCommand = executeNpmCommand;
exports.executeNpmRequest = executeNpmRequest;
exports.verifyIntegrity = verifyIntegrity;
exports.checkIfVersionExistsOrThrow = checkIfVersionExistsOrThrow;
const axios_1 = __importDefault(require("axios"));
const n8n_workflow_1 = require("n8n-workflow");
const semver_1 = require("semver");
const node_child_process_1 = require("node:child_process");
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const node_util_1 = require("node:util");
const constants_1 = require("../../constants");
const asyncExecFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
const REQUEST_TIMEOUT = 30000;
const WINDOWS_NPM_CLI_RELATIVE_PATH = 'node_modules/npm/bin/npm-cli.js';
const WINDOWS_NPM_CLI_CANDIDATE_PATHS = [
WINDOWS_NPM_CLI_RELATIVE_PATH,
`../${WINDOWS_NPM_CLI_RELATIVE_PATH}`,
];
const WINDOWS_NPM_PREFIX_SCRIPT_RELATIVE_PATH = 'node_modules/npm/bin/npm-prefix.js';
let cachedWindowsNpmCliPath;
const NPM_ERROR_PATTERNS = {
PACKAGE_NOT_FOUND: [constants_1.NPM_COMMAND_TOKENS.NPM_PACKAGE_NOT_FOUND_ERROR],
NO_VERSION_AVAILABLE: [constants_1.NPM_COMMAND_TOKENS.NPM_NO_VERSION_AVAILABLE],
PACKAGE_VERSION_NOT_FOUND: [constants_1.NPM_COMMAND_TOKENS.NPM_PACKAGE_VERSION_NOT_FOUND_ERROR],
DISK_NO_SPACE: [constants_1.NPM_COMMAND_TOKENS.NPM_DISK_NO_SPACE],
DISK_INSUFFICIENT_SPACE: [constants_1.NPM_COMMAND_TOKENS.NPM_DISK_INSUFFICIENT_SPACE],
};
function isDnsError(error) {
const message = error instanceof Error ? error.message : String(error);
return message.includes('getaddrinfo') || message.includes('ENOTFOUND');
}
function isNpmExecErrorWithStdout(error) {
return typeof error === 'object' && error !== null && 'code' in error && 'stdout' in error;
}
function isNpmError(error) {
const message = error instanceof Error ? error.message : String(error);
return (message.includes('npm ERR!') ||
message.includes('E404') ||
message.includes('404 Not Found') ||
message.includes('ENOTFOUND'));
}
function sanitizeRegistryUrl(registryUrl) {
return registryUrl.replace(/\/+$/, '');
}
function matchesErrorPattern(message, patterns) {
return patterns.some((pattern) => message.includes(pattern));
}
async function pathExists(path) {
try {
await (0, promises_1.access)(path);
return true;
}
catch {
return false;
}
}
function stdoutToString(stdout, trim = false) {
const value = typeof stdout === 'string' ? stdout : stdout.toString();
return trim ? value.trim() : value;
}
async function resolveWindowsDefaultNpmCliPath(nodeDirectory) {
for (const relativePath of WINDOWS_NPM_CLI_CANDIDATE_PATHS) {
const candidatePath = (0, node_path_1.join)(nodeDirectory, relativePath);
if (await pathExists(candidatePath)) {
return candidatePath;
}
}
return undefined;
}
async function resolveWindowsPrefixNpmCliPath(nodeDirectory) {
const npmPrefixScriptPath = (0, node_path_1.join)(nodeDirectory, WINDOWS_NPM_PREFIX_SCRIPT_RELATIVE_PATH);
if (!(await pathExists(npmPrefixScriptPath))) {
return undefined;
}
try {
const { stdout } = await asyncExecFile(process.execPath, [npmPrefixScriptPath]);
const globalPrefix = stdoutToString(stdout, true);
if (!globalPrefix) {
return undefined;
}
const globalPrefixNpmCliPath = (0, node_path_1.join)(globalPrefix, WINDOWS_NPM_CLI_RELATIVE_PATH);
if ((0, node_path_1.isAbsolute)(globalPrefixNpmCliPath) && (await pathExists(globalPrefixNpmCliPath))) {
return globalPrefixNpmCliPath;
}
}
catch {
}
return undefined;
}
async function resolveWindowsNpmCliPath() {
if (cachedWindowsNpmCliPath) {
return cachedWindowsNpmCliPath;
}
const nodeDirectory = (0, node_path_1.dirname)(process.execPath);
const prefixNpmCliPath = await resolveWindowsPrefixNpmCliPath(nodeDirectory);
if (prefixNpmCliPath) {
cachedWindowsNpmCliPath = prefixNpmCliPath;
return cachedWindowsNpmCliPath;
}
const defaultNpmCliPath = await resolveWindowsDefaultNpmCliPath(nodeDirectory);
if (defaultNpmCliPath) {
cachedWindowsNpmCliPath = defaultNpmCliPath;
return cachedWindowsNpmCliPath;
}
throw new n8n_workflow_1.UnexpectedError('Failed to locate npm CLI. Please ensure npm is installed.');
}
async function executeNpmCli(args, cwd) {
if (process.platform === 'win32') {
const npmCliPath = await resolveWindowsNpmCliPath();
const { stdout } = await asyncExecFile(process.execPath, [npmCliPath, ...args], cwd ? { cwd } : undefined);
return stdoutToString(stdout);
}
const { stdout } = await asyncExecFile('npm', args, cwd ? { cwd } : undefined);
return stdoutToString(stdout);
}
function redactAuthTokens(text) {
return text.replace(/_authToken=\S+/g, '_authToken=*****');
}
function registryAuthKey(registryUrl) {
const url = new URL(registryUrl);
const pathname = url.pathname.replace(/\/+$/, '');
return `${url.host}${pathname}`;
}
async function executeNpmCommand(args, options = {}) {
const { cwd, doNotHandleError, registry, authToken } = options;
const registryArgs = [];
if (registry) {
const sanitized = sanitizeRegistryUrl(registry);
registryArgs.push(`--registry=${sanitized}`);
if (authToken)
registryArgs.push(`--//${registryAuthKey(sanitized)}/:_authToken=${authToken}`);
}
const fullArgs = [...args, ...registryArgs];
n8n_workflow_1.LoggerProxy.debug('Executing npm command', {
args: fullArgs.map((a) => (a.includes('_authToken=') ? a.replace(/=.+$/, '=*****') : a)),
cwd,
});
try {
return await executeNpmCli(fullArgs, cwd);
}
catch (error) {
if (authToken && error instanceof Error) {
error.message = redactAuthTokens(error.message);
}
if (doNotHandleError) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : String(error);
n8n_workflow_1.LoggerProxy.warn('Failed to execute npm command', { errorMessage });
if (matchesErrorPattern(errorMessage, NPM_ERROR_PATTERNS.PACKAGE_NOT_FOUND)) {
throw new n8n_workflow_1.UnexpectedError(constants_1.RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND);
}
if (matchesErrorPattern(errorMessage, NPM_ERROR_PATTERNS.NO_VERSION_AVAILABLE)) {
throw new n8n_workflow_1.UnexpectedError(constants_1.RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND);
}
if (matchesErrorPattern(errorMessage, NPM_ERROR_PATTERNS.PACKAGE_VERSION_NOT_FOUND)) {
throw new n8n_workflow_1.UnexpectedError(constants_1.RESPONSE_ERROR_MESSAGES.PACKAGE_VERSION_NOT_FOUND);
}
if (matchesErrorPattern(errorMessage, NPM_ERROR_PATTERNS.DISK_NO_SPACE) ||
matchesErrorPattern(errorMessage, NPM_ERROR_PATTERNS.DISK_INSUFFICIENT_SPACE)) {
throw new n8n_workflow_1.UnexpectedError(constants_1.RESPONSE_ERROR_MESSAGES.DISK_IS_FULL);
}
if (isDnsError(error)) {
throw new n8n_workflow_1.UnexpectedError('Network error: Unable to reach npm registry. Please check your internet connection.');
}
throw new n8n_workflow_1.UnexpectedError('Failed to execute npm command', { cause: error });
}
}
async function executeNpmRequest(registryUrl, path, options = {}) {
const { authToken, headers: extraHeaders, timeout = REQUEST_TIMEOUT } = options;
const url = `${sanitizeRegistryUrl(registryUrl)}/${path}`;
const authHeaders = authToken ? { ['Authorization']: `Bearer ${authToken}` } : {};
const headers = { ...extraHeaders, ...authHeaders };
const redactedHeaders = authToken
? { ...headers, Authorization: 'Bearer *****' }
: { ...headers };
n8n_workflow_1.LoggerProxy.debug('Executing npm registry request', { url, headers: redactedHeaders, timeout });
try {
const { data } = await axios_1.default.get(url, {
timeout,
headers: Object.keys(headers).length > 0 ? headers : undefined,
});
return data;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
n8n_workflow_1.LoggerProxy.warn('Failed to execute npm registry request', { url, errorMessage });
throw error;
}
}
async function verifyIntegrity(packageName, version, registryUrl, expectedIntegrity, authToken) {
const path = `${encodeURIComponent(packageName)}/${version}`;
try {
const metadata = await executeNpmRequest(registryUrl, path, {
authToken,
});
const integrity = metadata?.dist?.integrity;
if (integrity !== expectedIntegrity) {
throw new n8n_workflow_1.UnexpectedError('Checksum verification failed. Package integrity does not match. Try restarting n8n and attempting the installation again.');
}
return;
}
catch (error) {
try {
const stdout = await executeNpmCommand(['view', `${packageName}@${version}`, 'dist.integrity', '--json'], { doNotHandleError: true, registry: registryUrl, authToken });
const integrity = (0, n8n_workflow_1.jsonParse)(stdout);
if (integrity !== expectedIntegrity) {
throw new n8n_workflow_1.UnexpectedError('Checksum verification failed. Package integrity does not match. Try restarting n8n and attempting the installation again.');
}
return;
}
catch (cliError) {
if (isDnsError(cliError) || isNpmError(cliError)) {
throw new n8n_workflow_1.UnexpectedError('Checksum verification failed. Please check your network connection and try again.');
}
throw new n8n_workflow_1.UnexpectedError('Checksum verification failed. Try restarting n8n and attempting the installation again.');
}
}
}
async function checkIfVersionExistsOrThrow(packageName, version, registryUrl, authToken) {
const path = `${encodeURIComponent(packageName)}/${version}`;
try {
await executeNpmRequest(registryUrl, path, { authToken });
return true;
}
catch (error) {
try {
const stdout = await executeNpmCommand(['view', `${packageName}@${version}`, 'version', '--json'], { doNotHandleError: true, registry: registryUrl, authToken });
const resolvedVersion = (0, n8n_workflow_1.jsonParse)(stdout);
const isResolvedSemver = typeof resolvedVersion === 'string' && (0, semver_1.valid)(resolvedVersion) !== null;
const isExactSemver = (0, semver_1.valid)(version) !== null;
if (isResolvedSemver && (!isExactSemver || resolvedVersion)) {
return true;
}
throw new n8n_workflow_1.UnexpectedError('Failed to check package version existence');
}
catch (cliError) {
const message = cliError instanceof Error ? cliError.message : String(cliError);
if (message.includes('E404') || message.includes('404 Not Found')) {
throw new n8n_workflow_1.UnexpectedError('Package version does not exist');
}
if (isDnsError(cliError) || isNpmError(cliError)) {
throw new n8n_workflow_1.UnexpectedError('The community nodes service is temporarily unreachable. Please try again later.');
}
throw new n8n_workflow_1.UnexpectedError('Failed to check package version existence');
}
}
}
//# sourceMappingURL=npm-utils.js.map