UNPKG

n8n

Version:

n8n Workflow Automation Tool

256 lines 12 kB
"use strict"; 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