n8n-nodes-base
Version:
Base nodes of n8n
412 lines • 15.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateRequiredStringField = validateRequiredStringField;
exports.validateSessionId = validateSessionId;
exports.validateSessionAndWindowId = validateSessionAndWindowId;
exports.validateProfileName = validateProfileName;
exports.validateTimeoutMinutes = validateTimeoutMinutes;
exports.validateUrl = validateUrl;
exports.validateProxy = validateProxy;
exports.validateScrollByAmount = validateScrollByAmount;
exports.validateScrollingMode = validateScrollingMode;
exports.validateScreenResolution = validateScreenResolution;
exports.validateSaveProfileOnTermination = validateSaveProfileOnTermination;
exports.validateAirtopApiResponse = validateAirtopApiResponse;
exports.convertScreenshotToBinary = convertScreenshotToBinary;
exports.shouldCreateNewSession = shouldCreateNewSession;
exports.createSession = createSession;
exports.createSessionAndWindow = createSessionAndWindow;
exports.waitForSessionEvent = waitForSessionEvent;
const n8n_workflow_1 = require("n8n-workflow");
const n8n_workflow_2 = require("n8n-workflow");
const fields_1 = require("./actions/common/fields");
const constants_1 = require("./constants");
const constants_2 = require("./constants");
const transport_1 = require("./transport");
/**
* Validate a required string field
* @param this - The execution context
* @param index - The index of the node
* @param field - The field to validate
* @param fieldName - The name of the field
*/
function validateRequiredStringField(index, field, fieldName) {
let value = this.getNodeParameter(field, index, '');
value = (value || '').trim();
const errorMessage = constants_2.ERROR_MESSAGES.REQUIRED_PARAMETER.replace('{{field}}', fieldName);
if (!value) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), errorMessage, {
itemIndex: index,
});
}
return value;
}
/**
* Validate the session ID parameter
* @param this - The execution context
* @param index - The index of the node
* @returns The validated session ID
*/
function validateSessionId(index) {
let sessionId = this.getNodeParameter('sessionId', index, '');
sessionId = (sessionId || '').trim();
if (!sessionId) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.SESSION_ID_REQUIRED, {
itemIndex: index,
});
}
return sessionId;
}
/**
* Validate the session ID and window ID parameters
* @param this - The execution context
* @param index - The index of the node
* @returns The validated session ID and window ID parameters
*/
function validateSessionAndWindowId(index) {
let sessionId = this.getNodeParameter('sessionId', index, '');
let windowId = this.getNodeParameter('windowId', index, '');
sessionId = (sessionId || '').trim();
windowId = (windowId || '').trim();
if (!sessionId) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.SESSION_ID_REQUIRED, {
itemIndex: index,
});
}
if (!windowId) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.WINDOW_ID_REQUIRED, {
itemIndex: index,
});
}
return {
sessionId,
windowId,
};
}
/**
* Validate the profile name parameter
* @param this - The execution context
* @param index - The index of the node
* @returns The validated profile name
*/
function validateProfileName(index) {
let profileName = this.getNodeParameter('profileName', index);
profileName = (profileName || '').trim();
if (!profileName) {
return profileName;
}
if (!/^[a-zA-Z0-9-]+$/.test(profileName)) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.PROFILE_NAME_INVALID, {
itemIndex: index,
});
}
return profileName;
}
/**
* Validate the timeout minutes parameter
* @param this - The execution context
* @param index - The index of the node
* @returns The validated timeout minutes
*/
function validateTimeoutMinutes(index) {
const timeoutMinutes = this.getNodeParameter('timeoutMinutes', index, constants_2.DEFAULT_TIMEOUT_MINUTES);
if (timeoutMinutes < constants_2.MIN_TIMEOUT_MINUTES || timeoutMinutes > constants_2.MAX_TIMEOUT_MINUTES) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.TIMEOUT_MINUTES_INVALID, {
itemIndex: index,
});
}
return timeoutMinutes;
}
/**
* Validate the URL parameter
* @param this - The execution context
* @param index - The index of the node
* @returns The validated URL
*/
function validateUrl(index) {
let url = this.getNodeParameter('url', index);
url = (url || '').trim();
if (!url) {
return '';
}
if (!url.startsWith('http')) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.URL_INVALID, {
itemIndex: index,
});
}
return url;
}
/**
* Validate the Proxy configuration
* @param this - The execution context
* @param index - The index of the node
* @returns The validated proxy configuration
*/
function validateProxy(index) {
const proxyParam = this.getNodeParameter('proxy', index, '');
const proxyConfig = this.getNodeParameter('proxyConfig', index, '');
const isConfigEmpty = Object.keys(proxyConfig).length === 0;
if (proxyParam === 'integrated') {
return {
proxy: isConfigEmpty ? true : { ...proxyConfig },
};
}
// handle custom proxy configuration
if (proxyParam === 'proxyUrl') {
return {
proxy: validateRequiredStringField.call(this, index, 'proxyUrl', 'Proxy URL'),
};
}
return {
proxy: false,
};
}
/**
* Validate the scrollBy amount parameter
* @param this - The execution context
* @param index - The index of the node
* @param parameterName - The name of the parameter
* @returns The validated scrollBy amount
*/
function validateScrollByAmount(index, parameterName) {
const regex = /^(?:-?\d{1,3}(?:%|px))$/;
const scrollBy = this.getNodeParameter(parameterName, index, {});
if (!scrollBy?.xAxis && !scrollBy?.yAxis) {
return {};
}
const allAxisValid = [scrollBy.xAxis, scrollBy.yAxis]
.filter(Boolean)
.every((axis) => regex.test(axis ?? ''));
if (!allAxisValid) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.SCROLL_BY_AMOUNT_INVALID, {
itemIndex: index,
});
}
return scrollBy;
}
/**
* Validate the scroll mode parameter
* @param this - The execution context
* @param index - The index of the node
* @returns Scroll mode
* @throws Error if the scroll mode or scroll parameters are invalid
*/
function validateScrollingMode(index) {
const scrollingMode = this.getNodeParameter('scrollingMode', index, 'automatic');
const scrollToEdge = this.getNodeParameter('scrollToEdge.edgeValues', index, {});
const scrollBy = this.getNodeParameter('scrollBy.scrollValues', index, {});
if (scrollingMode !== 'manual') {
return scrollingMode;
}
// validate manual scroll parameters
const emptyScrollBy = !scrollBy.xAxis && !scrollBy.yAxis;
const emptyScrollToEdge = !scrollToEdge.xAxis && !scrollToEdge.yAxis;
if (emptyScrollBy && emptyScrollToEdge) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.SCROLL_MODE_INVALID, {
itemIndex: index,
});
}
return scrollingMode;
}
/**
* Validate the screen resolution parameter
* @param this - The execution context
* @param index - The index of the node
* @returns The validated screen resolution
*/
function validateScreenResolution(index) {
let screenResolution = this.getNodeParameter('screenResolution', index, '');
screenResolution = (screenResolution || '').trim().toLowerCase();
const regex = /^\d{3,4}x\d{3,4}$/; // Expected format: 1280x720
if (!screenResolution) {
return '';
}
if (!regex.test(screenResolution)) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.SCREEN_RESOLUTION_INVALID, {
itemIndex: index,
});
}
return screenResolution;
}
/**
* Validate the save profile on termination parameter
* @param this - The execution context
* @param index - The index of the node
* @param profileName - The profile name
* @returns The validated save profile on termination
*/
function validateSaveProfileOnTermination(index, profileName) {
const saveProfileOnTermination = this.getNodeParameter('saveProfileOnTermination', index, false);
if (saveProfileOnTermination && !profileName) {
throw new n8n_workflow_2.NodeOperationError(this.getNode(), constants_2.ERROR_MESSAGES.PROFILE_NAME_REQUIRED, {
itemIndex: index,
});
}
return saveProfileOnTermination;
}
/**
* Check if there is an error in the API response and throw NodeApiError
* @param node - The node instance
* @param response - The response from the API
*/
function validateAirtopApiResponse(node, response) {
if (response?.errors?.length) {
const errorMessage = response.errors.map((error) => error.message).join('\n');
throw new n8n_workflow_1.NodeApiError(node, {
message: errorMessage,
});
}
}
/**
* Convert a screenshot from the API response to a binary buffer
* @param screenshot - The screenshot from the API response
* @returns The processed screenshot
*/
function convertScreenshotToBinary(screenshot) {
const base64Data = screenshot.dataUrl.replace('data:image/jpeg;base64,', '');
const buffer = Buffer.from(base64Data, 'base64');
return buffer;
}
/**
* Check if a new session should be created
* @param this - The execution context
* @param index - The index of the node
* @returns True if a new session should be created, false otherwise
*/
function shouldCreateNewSession(index) {
const sessionMode = this.getNodeParameter('sessionMode', index);
return Boolean(sessionMode && sessionMode === fields_1.SESSION_MODE.NEW);
}
/**
* Create a new session and wait until the session is ready
* @param this - The execution context
* @param parameters - The parameters for the session
* @returns The session ID
*/
async function createSession(parameters, timeout = constants_2.OPERATION_TIMEOUT) {
// Request session creation
const response = (await transport_1.apiRequest.call(this, 'POST', '/sessions', parameters));
const sessionId = response?.data?.id;
if (!sessionId) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: 'Failed to create session',
code: 500,
});
}
// Poll until the session is ready or timeout is reached
let sessionStatus = response?.data?.status;
const startTime = Date.now();
while (sessionStatus !== constants_2.SESSION_STATUS.RUNNING) {
if (Date.now() - startTime > timeout) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: constants_2.ERROR_MESSAGES.TIMEOUT_REACHED,
code: 500,
});
}
await new Promise((resolve) => setTimeout(resolve, 1000));
const sessionStatusResponse = (await transport_1.apiRequest.call(this, 'GET', `/sessions/${sessionId}`));
sessionStatus = sessionStatusResponse.data.status;
}
return {
sessionId,
data: {
...response,
},
};
}
/**
* Create a new session and window
* @param this - The execution context
* @param index - The index of the node
* @returns The session ID and window ID
*/
async function createSessionAndWindow(index) {
const node = this.getNode();
const profileName = validateProfileName.call(this, index);
const url = validateRequiredStringField.call(this, index, 'url', 'URL');
const { sessionId } = await createSession.call(this, {
configuration: {
profileName,
},
});
if (!sessionId) {
throw new n8n_workflow_1.NodeApiError(node, {
message: 'Failed to create session',
code: 500,
});
}
this.logger.info(`[${node.name}] Session successfully created.`);
const windowResponse = await transport_1.apiRequest.call(this, 'POST', `/sessions/${sessionId}/windows`, {
url,
});
const windowId = windowResponse?.data?.windowId;
if (!windowId) {
throw new n8n_workflow_1.NodeApiError(node, {
message: 'Failed to create window',
code: 500,
});
}
this.logger.info(`[${node.name}] Window successfully created.`);
return { sessionId, windowId };
}
/**
* SSE Helpers
*/
/**
* Parses a server event from a string
* @param eventText - The string to parse
* @returns The parsed event or null if the string is not a valid event
*/
function parseEvent(eventText) {
const dataLine = eventText.split('\n').find((line) => line.startsWith('data:'));
if (!dataLine) {
return null;
}
const jsonStr = dataLine.replace('data: ', '').trim();
return (0, n8n_workflow_1.jsonParse)(jsonStr, {
errorMessage: 'Failed to parse server event',
});
}
/**
* Waits for a session event to occur
* @param this - The execution context providing access to n8n functionality
* @param sessionId - ID of the session to check for events
* @param condition - Function to check if the event meets the condition
* @param timeoutInSeconds - Maximum time in seconds to wait before failing (defaults to DEFAULT_DOWNLOAD_TIMEOUT_SECONDS)
* @returns Promise resolving to the event when the condition is met
*/
async function waitForSessionEvent(sessionId, condition, timeoutInSeconds = constants_2.DEFAULT_DOWNLOAD_TIMEOUT_SECONDS) {
const url = `${constants_1.BASE_URL}/sessions/${sessionId}/events?all=true`;
let stream;
const eventPromise = new Promise(async (resolve) => {
stream = (await this.helpers.httpRequestWithAuthentication.call(this, 'airtopApi', {
method: 'GET',
url,
encoding: 'stream',
}));
stream.on('data', (data) => {
const event = parseEvent(data.toString());
if (!event) {
return;
}
// handle event
if (condition(event)) {
stream.removeAllListeners();
resolve(event);
return;
}
});
});
const timeoutPromise = new Promise((_resolve, reject) => {
setTimeout(() => {
reject(new n8n_workflow_1.NodeApiError(this.getNode(), {
message: constants_2.ERROR_MESSAGES.TIMEOUT_REACHED,
code: 500,
}));
stream.removeAllListeners();
}, timeoutInSeconds * 1000);
});
const result = await Promise.race([eventPromise, timeoutPromise]);
return result;
}
//# sourceMappingURL=GenericFunctions.js.map