ghost-cursor-patchright-core
Version:
Move your mouse like a human in puppeteer and playwright or generate realistic movements on any 2D plane
775 lines (774 loc) • 43.5 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCursor = exports.path = exports.getRandomPagePoint = exports.installMouseHelper = void 0;
var debug_1 = __importDefault(require("debug"));
var math_1 = require("./math");
var mouse_helper_1 = require("./mouse-helper");
Object.defineProperty(exports, "installMouseHelper", { enumerable: true, get: function () { return __importDefault(mouse_helper_1).default; } });
var log = (0, debug_1.default)('ghost-cursor');
/** Helper function to wait a specified number of milliseconds */
var delay = function (ms) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (ms < 1)
return [2 /*return*/];
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, ms); })];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); };
/**
* Calculate the amount of time needed to move from (x1, y1) to (x2, y2)
* given the width of the element being clicked on
* https://en.wikipedia.org/wiki/Fitts%27s_law
*/
var fitts = function (distance, width) {
var a = 0;
var b = 2;
var ratio = Math.max(distance / Math.max(width, 1), 0);
var id = Math.log2(ratio + 1);
return a + b * id;
};
/** Get a random point on a box */
var getRandomBoxPoint = function (_a, options) {
var x = _a.x, y = _a.y, width = _a.width, height = _a.height;
var paddingWidth = 0;
var paddingHeight = 0;
if ((options === null || options === void 0 ? void 0 : options.paddingPercentage) !== undefined &&
options.paddingPercentage >= 0 &&
options.paddingPercentage <= 100) {
paddingWidth = (width * options.paddingPercentage) / 100;
paddingHeight = (height * options.paddingPercentage) / 100;
}
var effectiveWidth = Math.max(0, width - paddingWidth);
var effectiveHeight = Math.max(0, height - paddingHeight);
return {
x: x + paddingWidth / 2 + Math.random() * effectiveWidth,
y: y + paddingHeight / 2 + Math.random() * effectiveHeight
};
};
/** Updated helper to create a CDP session in Playwright - Always uses Page */
var getCDPClient = function (page) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, page.context().newCDPSession(page)];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); };
/** Get a random point on a browser page using viewport size - Always uses Page */
var getRandomPagePoint = function (page) { return __awaiter(void 0, void 0, void 0, function () {
var viewport;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
viewport = page.viewportSize();
if (!(viewport == null)) return [3 /*break*/, 2];
return [4 /*yield*/, page.evaluate(function () { return ({
width: window.innerWidth,
height: window.innerHeight
}); })];
case 1:
viewport = _a.sent();
if (viewport == null) {
throw new Error('Could not get viewport size');
}
_a.label = 2;
case 2: return [2 /*return*/, getRandomBoxPoint({
x: 0,
y: 0,
width: viewport.width,
height: viewport.height
})];
}
});
}); };
exports.getRandomPagePoint = getRandomPagePoint;
/** Get the bounding box of an element relative to the main frame using boundingBox */
var getElementBox = function (element) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, element.boundingBox()];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); };
var boundingBoxWithFallback = function (elem) { return __awaiter(void 0, void 0, void 0, function () {
var scrollError_1, message, i, box;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
return [4 /*yield*/, elem.scrollIntoViewIfNeeded({ timeout: 2000 })];
case 1:
_a.sent();
return [4 /*yield*/, delay(100 + Math.random() * 50)]; // Wait for scrolling to settle
case 2:
_a.sent(); // Wait for scrolling to settle
return [3 /*break*/, 4];
case 3:
scrollError_1 = _a.sent();
message = scrollError_1 instanceof Error ? scrollError_1.message : String(scrollError_1);
log("Warning: scrollIntoViewIfNeeded failed, but proceeding: ".concat(message));
return [3 /*break*/, 4];
case 4:
i = 0;
_a.label = 5;
case 5:
if (!(i < 3)) return [3 /*break*/, 9];
return [4 /*yield*/, getElementBox(elem)];
case 6:
box = _a.sent();
if (box != null) {
return [2 /*return*/, box];
}
log("Element has no bounding box. Attempt #".concat(i + 1, "."));
return [4 /*yield*/, delay(200 + Math.random() * 100)]; // Wait before retrying
case 7:
_a.sent(); // Wait before retrying
_a.label = 8;
case 8:
i++;
return [3 /*break*/, 5];
case 9: throw new Error('Element is not visible or does not have a bounding box after attempting to scroll and retry.');
}
});
}); };
function path(start, end, options) {
var optionsResolved = typeof options === 'number'
? { spreadOverride: options, noiseScale: 1.5, endpointNoise: false }
: __assign({ noiseScale: 1.5, endpointNoise: false }, options); // Default noise
var DEFAULT_WIDTH = 100;
var MIN_STEPS = 25;
var width = typeof end === 'object' && 'width' in end && end.width !== 0 ? end.width : DEFAULT_WIDTH;
var curve = (0, math_1.bezierCurve)(start, end, optionsResolved.spreadOverride);
var length = curve.length() * 0.8;
var speedFactor = optionsResolved.moveSpeed !== undefined && optionsResolved.moveSpeed > 0
? 1 / optionsResolved.moveSpeed
: 1;
var baseTime = MIN_STEPS;
var fittsID = fitts(length, width);
var steps = Math.ceil(Math.max(MIN_STEPS, (baseTime + fittsID * 5) * speedFactor)); // Ensure min steps
var re = curve.getLUT(steps);
return clampPositive(re, optionsResolved);
}
exports.path = path;
var clampPositive = function (vectors, options) {
var _a;
var noiseScale = (_a = options === null || options === void 0 ? void 0 : options.noiseScale) !== null && _a !== void 0 ? _a : 0;
var noisedVectors = vectors.map(function (vector, index) {
var _a;
var isEndpoint = index === 0 || index === vectors.length - 1;
if (noiseScale > 0 && (((_a = options === null || options === void 0 ? void 0 : options.endpointNoise) !== null && _a !== void 0 ? _a : false) || !isEndpoint)) {
return (0, math_1.addNoise)(vector, noiseScale);
}
return vector;
});
var clampedVectors = noisedVectors.map(function (vector) { return ({
x: Math.max(0, vector.x),
y: Math.max(0, vector.y)
}); });
return (options === null || options === void 0 ? void 0 : options.useTimestamps) === true ? generateTimestamps(clampedVectors, options) : clampedVectors;
};
var generateTimestamps = function (vectors, options) {
var _a;
var DEFAULT_MOVE_SPEED = 1;
var moveSpeed = (_a = options === null || options === void 0 ? void 0 : options.moveSpeed) !== null && _a !== void 0 ? _a : DEFAULT_MOVE_SPEED;
var totalSteps = vectors.length;
var avgTimePerStep = 15 / Math.max(moveSpeed, 0.1);
var totalDuration = totalSteps * avgTimePerStep;
var timedVectors = [];
var startTime = Date.now();
if (totalSteps <= 1) {
return totalSteps === 1 ? [__assign(__assign({}, vectors[0]), { timestamp: startTime })] : [];
}
for (var i = 0; i < totalSteps; i++) {
var progress = i / (totalSteps - 1);
var easedProgress = (0, math_1.easeInOutCubic)(progress);
var timestamp = startTime + Math.round(easedProgress * totalDuration);
timedVectors.push(__assign(__assign({}, vectors[i]), { timestamp: timestamp }));
}
return timedVectors;
};
var shouldOvershoot = function (a, b, threshold) {
return (0, math_1.magnitude)((0, math_1.direction)(a, b)) > threshold;
};
var intersectsElement = function (vec, box) {
return (vec.x >= box.x &&
vec.x <= box.x + box.width &&
vec.y >= box.y &&
vec.y <= box.y + box.height);
};
var createCursor = function (page, start, performRandomMovesInitially, defaultOptions, frame) {
if (start === void 0) { start = math_1.origin; }
if (performRandomMovesInitially === void 0) { performRandomMovesInitially = false; }
if (defaultOptions === void 0) { defaultOptions = {}; }
if (frame === void 0) { frame = null; }
var OVERSHOOT_SPREAD = 10;
var OVERSHOOT_RADIUS = 120;
var previous = start;
var isMoving = false;
var isPerformingRandomMoves = performRandomMovesInitially;
var randomMoveTimeoutId = null;
var context = frame !== null && frame !== void 0 ? frame : page;
var cdpClientPromise = null;
var getOrCreateCDPClient = function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (cdpClientPromise == null) {
cdpClientPromise = getCDPClient(page).catch(function (err) {
log('Failed to create CDP Client', err);
cdpClientPromise = null;
throw err;
});
}
return [4 /*yield*/, cdpClientPromise];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); };
var tracePath = function (vectors, abortOnMove) {
if (abortOnMove === void 0) { abortOnMove = false; }
return __awaiter(void 0, void 0, void 0, function () {
var cdpClient, vectors_1, vectors_1_1, v, dispatchParams, error_1, isError, errorMessage, e_1_1, error_2;
var e_1, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
isMoving = true;
_b.label = 1;
case 1:
_b.trys.push([1, 13, 14, 15]);
return [4 /*yield*/, getOrCreateCDPClient()];
case 2:
cdpClient = _b.sent();
_b.label = 3;
case 3:
_b.trys.push([3, 10, 11, 12]);
vectors_1 = __values(vectors), vectors_1_1 = vectors_1.next();
_b.label = 4;
case 4:
if (!!vectors_1_1.done) return [3 /*break*/, 9];
v = vectors_1_1.value;
if (page.isClosed()) {
log('Page closed during tracePath, aborting.');
isMoving = false;
return [2 /*return*/];
}
_b.label = 5;
case 5:
_b.trys.push([5, 7, , 8]);
dispatchParams = {
type: 'mouseMoved',
x: v.x,
y: v.y
};
if ('timestamp' in v && typeof v.timestamp === 'number' && v.timestamp > 0) {
dispatchParams.timestamp = v.timestamp / 1000;
}
return [4 /*yield*/, cdpClient.send('Input.dispatchMouseEvent', dispatchParams)];
case 6:
_b.sent();
previous = { x: v.x, y: v.y };
return [3 /*break*/, 8];
case 7:
error_1 = _b.sent();
isError = error_1 instanceof Error;
errorMessage = isError ? error_1.message : String(error_1);
if (page.isClosed() || (isError && (errorMessage.includes('Target closed') || errorMessage.includes('Session closed')))) {
log('Warning: could not move mouse, page or session closed.');
isMoving = false;
return [2 /*return*/];
}
log('Warning: could not move mouse, error message:', errorMessage);
return [3 /*break*/, 8];
case 8:
vectors_1_1 = vectors_1.next();
return [3 /*break*/, 4];
case 9: return [3 /*break*/, 12];
case 10:
e_1_1 = _b.sent();
e_1 = { error: e_1_1 };
return [3 /*break*/, 12];
case 11:
try {
if (vectors_1_1 && !vectors_1_1.done && (_a = vectors_1.return)) _a.call(vectors_1);
}
finally { if (e_1) throw e_1.error; }
return [7 /*endfinally*/];
case 12: return [3 /*break*/, 15];
case 13:
error_2 = _b.sent();
log('Error during tracePath setup or execution:', error_2);
return [3 /*break*/, 15];
case 14:
if (isMoving) {
isMoving = false;
}
return [7 /*endfinally*/];
case 15: return [2 /*return*/];
}
});
});
};
var _toggleRandomMove = function (random) {
if (isPerformingRandomMoves !== random) {
isPerformingRandomMoves = random;
if (random) {
log('Random moves enabled');
_scheduleRandomMove(defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.randomMove);
}
else {
log('Random moves disabled');
if (randomMoveTimeoutId != null) {
clearTimeout(randomMoveTimeoutId);
randomMoveTimeoutId = null;
}
}
}
};
var _scheduleRandomMove = function (options) {
var _a, _b;
if (randomMoveTimeoutId != null) {
clearTimeout(randomMoveTimeoutId);
}
if (!isPerformingRandomMoves || isMoving) {
return;
}
var optionsResolved = __assign(__assign({ moveDelay: 2000, randomizeMoveDelay: true, moveSpeed: 0.8 }, defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.randomMove), options);
var delayTime = ((_a = optionsResolved.moveDelay) !== null && _a !== void 0 ? _a : 0) * (((_b = optionsResolved.randomizeMoveDelay) !== null && _b !== void 0 ? _b : false) ? (0.5 + Math.random() * 0.5) : 1);
randomMoveTimeoutId = setTimeout(function () {
if (!isPerformingRandomMoves || isMoving || page.isClosed()) {
return;
}
var performRandomMove = function () { return __awaiter(void 0, void 0, void 0, function () {
var randPoint, error_3, message;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
return [4 /*yield*/, (0, exports.getRandomPagePoint)(page)];
case 1:
randPoint = _a.sent();
log("Performing random move to ".concat(JSON.stringify(randPoint)));
return [4 /*yield*/, actions.moveTo(randPoint, {
moveSpeed: optionsResolved.moveSpeed,
moveDelay: 0,
randomizeMoveDelay: false
})];
case 2:
_a.sent();
if (isPerformingRandomMoves) {
_scheduleRandomMove(options);
}
return [3 /*break*/, 4];
case 3:
error_3 = _a.sent();
if (!page.isClosed()) {
message = error_3 instanceof Error ? error_3.message : String(error_3);
log('Warning: failed during random move, stopping random moves.', message);
_toggleRandomMove(false);
}
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); };
performRandomMove().catch(function (error) {
log('Error in random move:', error);
});
}, delayTime);
};
var _startAction = function () { return __awaiter(void 0, void 0, void 0, function () {
var wasRandomActive;
return __generator(this, function (_a) {
wasRandomActive = isPerformingRandomMoves;
if (wasRandomActive) {
_toggleRandomMove(false);
}
if (randomMoveTimeoutId != null) {
clearTimeout(randomMoveTimeoutId);
randomMoveTimeoutId = null;
}
isMoving = true;
return [2 /*return*/, wasRandomActive];
});
}); };
var _endAction = function (wasRandomActive, moveDelayOptions) { return __awaiter(void 0, void 0, void 0, function () {
var delayTime;
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
isMoving = false;
delayTime = ((_a = moveDelayOptions.moveDelay) !== null && _a !== void 0 ? _a : 0) * (((_b = moveDelayOptions.randomizeMoveDelay) !== null && _b !== void 0 ? _b : false) ? Math.random() : 1);
return [4 /*yield*/, delay(delayTime)];
case 1:
_c.sent();
if (wasRandomActive && !isMoving) {
_toggleRandomMove(true);
}
return [2 /*return*/];
}
});
}); };
var actions = {
toggleRandomMove: _toggleRandomMove,
getLocation: function () {
return previous;
},
click: function (selector, options) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function () {
var optionsResolved, wasRandom, cdpClient, targetPoint, locator, boundingBox, offset, relOffset, clampedOffset, finalBox, finalClickPoint, newBox, error_4, errorMessage;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
optionsResolved = __assign(__assign(__assign({ waitForClick: 50, paddingPercentage: 10, moveDelay: 0, randomizeMoveDelay: true, waitForSelector: 10000, maxTries: 3, overshootThreshold: 500, moveSpeed: 1, microJitter: true, dynamicAdjustment: true }, defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.move), defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.click), options);
return [4 /*yield*/, _startAction()];
case 1:
wasRandom = _d.sent();
_d.label = 2;
case 2:
_d.trys.push([2, 19, 20, 22]);
return [4 /*yield*/, getOrCreateCDPClient()];
case 3:
cdpClient = _d.sent();
if (!(selector != null)) return [3 /*break*/, 14];
log("Clicking element: ".concat(selector));
return [4 /*yield*/, actions.move(selector, {
waitForSelector: optionsResolved.waitForSelector,
paddingPercentage: optionsResolved.paddingPercentage,
moveSpeed: optionsResolved.moveSpeed,
maxTries: optionsResolved.maxTries,
overshootThreshold: optionsResolved.overshootThreshold,
moveDelay: 0,
randomizeMoveDelay: false
})];
case 4:
_d.sent();
targetPoint = previous;
locator = context.locator(selector);
return [4 /*yield*/, locator.waitFor({ state: 'visible', timeout: optionsResolved.waitForSelector })];
case 5:
_d.sent();
return [4 /*yield*/, locator.boundingBox()];
case 6:
boundingBox = _d.sent();
if (boundingBox == null) {
throw new Error("Element ".concat(selector, " has no bounding box right before click"));
}
offset = {
x: targetPoint.x - boundingBox.x,
y: targetPoint.y - boundingBox.y
};
relOffset = {
x: offset.x / boundingBox.width,
y: offset.y / boundingBox.height
};
if (offset.x < 0 || offset.x > boundingBox.width ||
offset.y < 0 || offset.y > boundingBox.height) {
log("Warning: Cursor at ".concat(JSON.stringify(targetPoint), " is outside element bounds ").concat(JSON.stringify(boundingBox)));
clampedOffset = {
x: Math.max(0, Math.min(offset.x, boundingBox.width)),
y: Math.max(0, Math.min(offset.y, boundingBox.height))
};
log("Using clamped offset: ".concat(JSON.stringify(clampedOffset)));
offset.x = clampedOffset.x;
offset.y = clampedOffset.y;
}
else {
log("Performing click on element at exact cursor offset ".concat(JSON.stringify(offset)));
}
finalBox = boundingBox;
finalClickPoint = { x: boundingBox.x + offset.x, y: boundingBox.y + offset.y };
if (!(optionsResolved.dynamicAdjustment === true)) return [3 /*break*/, 9];
return [4 /*yield*/, locator.boundingBox()];
case 7:
newBox = _d.sent();
if (!(newBox !== null)) return [3 /*break*/, 9];
finalBox = newBox;
finalClickPoint = {
x: finalBox.x + relOffset.x * finalBox.width,
y: finalBox.y + relOffset.y * finalBox.height
};
if (!((0, math_1.magnitude)((0, math_1.direction)(previous, finalClickPoint)) > 5)) return [3 /*break*/, 9];
return [4 /*yield*/, tracePath(path(previous, finalClickPoint, { moveSpeed: ((_a = optionsResolved.moveSpeed) !== null && _a !== void 0 ? _a : 1) * 1.5, noiseScale: 0.5 }))];
case 8:
_d.sent();
_d.label = 9;
case 9: return [4 /*yield*/, tracePath([{ x: finalClickPoint.x, y: finalClickPoint.y }])]; // Ensure at final point
case 10:
_d.sent(); // Ensure at final point
return [4 /*yield*/, cdpClient.send('Input.dispatchMouseEvent', { type: 'mousePressed', x: finalClickPoint.x, y: finalClickPoint.y, button: 'left', clickCount: 1 })];
case 11:
_d.sent();
return [4 /*yield*/, delay((_b = optionsResolved.waitForClick) !== null && _b !== void 0 ? _b : 0)];
case 12:
_d.sent();
return [4 /*yield*/, cdpClient.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x: finalClickPoint.x, y: finalClickPoint.y, button: 'left', clickCount: 1 })];
case 13:
_d.sent();
return [3 /*break*/, 18];
case 14:
log("Performing click at current location: ".concat(JSON.stringify(previous)));
return [4 /*yield*/, cdpClient.send('Input.dispatchMouseEvent', { type: 'mousePressed', x: previous.x, y: previous.y, button: 'left', clickCount: 1 })];
case 15:
_d.sent();
return [4 /*yield*/, delay((_c = optionsResolved.waitForClick) !== null && _c !== void 0 ? _c : 0)];
case 16:
_d.sent();
return [4 /*yield*/, cdpClient.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x: previous.x, y: previous.y, button: 'left', clickCount: 1 })];
case 17:
_d.sent();
_d.label = 18;
case 18: return [3 /*break*/, 22];
case 19:
error_4 = _d.sent();
errorMessage = error_4 instanceof Error ? error_4.message : String(error_4);
log('Error: could not perform click, error message:', errorMessage);
throw new Error("Failed to click ".concat((selector != null) ? selector : 'current location', ". Error: ").concat(errorMessage));
case 20: return [4 /*yield*/, _endAction(wasRandom, optionsResolved)];
case 21:
_d.sent();
return [7 /*endfinally*/];
case 22: return [2 /*return*/];
}
});
});
},
move: function (selector, options) {
return __awaiter(this, void 0, void 0, function () {
var optionsResolved, wasRandom, success, lastError, go_1;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
optionsResolved = __assign(__assign({ maxTries: 3, overshootThreshold: 500, paddingPercentage: 25, moveDelay: 0, randomizeMoveDelay: true, waitForSelector: 10000, moveSpeed: 1 }, defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.move), options);
return [4 /*yield*/, _startAction()];
case 1:
wasRandom = _a.sent();
success = false;
lastError = null;
_a.label = 2;
case 2:
_a.trys.push([2, , 4, 6]);
go_1 = function (iteration) { return __awaiter(_this, void 0, void 0, function () {
var elem, box, height, width, destination, dimensions, overshooting, targetPoint, correction, finalPos, finalBox, poll, pollBox, correctionTarget, error_5;
var _a, _b, _c, _d, _e;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
if (page.isClosed()) {
log('Page closed during move, aborting.');
return [2 /*return*/, false];
}
if (iteration >= ((_a = optionsResolved.maxTries) !== null && _a !== void 0 ? _a : 3)) {
log("Could not move to element within ".concat((_b = optionsResolved.maxTries) !== null && _b !== void 0 ? _b : 3, " tries."));
lastError = new Error("Could not mouse-over element within ".concat((_c = optionsResolved.maxTries) !== null && _c !== void 0 ? _c : 3, " tries"));
return [2 /*return*/, false];
}
log("Move attempt ".concat((iteration + 1).toString(), " for selector: ").concat(selector));
elem = null;
_f.label = 1;
case 1:
_f.trys.push([1, 17, , 20]);
return [4 /*yield*/, context.waitForSelector(selector, {
state: 'visible',
timeout: optionsResolved.waitForSelector
})];
case 2:
elem = (_f.sent());
return [4 /*yield*/, boundingBoxWithFallback(elem)];
case 3:
box = _f.sent();
height = box.height, width = box.width;
destination = getRandomBoxPoint(box, { paddingPercentage: optionsResolved.paddingPercentage });
dimensions = { height: height, width: width };
log("Target element box: ".concat(JSON.stringify(box), ", moving to point: ").concat(JSON.stringify(destination)));
overshooting = shouldOvershoot(previous, destination, (_d = optionsResolved.overshootThreshold) !== null && _d !== void 0 ? _d : 500);
targetPoint = overshooting ? (0, math_1.overshoot)(destination, OVERSHOOT_RADIUS) : destination;
return [4 /*yield*/, tracePath(path(previous, targetPoint, optionsResolved), false)];
case 4:
_f.sent();
if (!overshooting) return [3 /*break*/, 6];
log('Overshooting, correcting path...');
correction = path(targetPoint, __assign(__assign({}, dimensions), destination), {
spreadOverride: OVERSHOOT_SPREAD,
moveSpeed: optionsResolved.moveSpeed
});
return [4 /*yield*/, tracePath(correction, false)];
case 5:
_f.sent();
_f.label = 6;
case 6:
finalPos = previous;
return [4 /*yield*/, getElementBox(elem)];
case 7:
finalBox = _f.sent();
if (!((finalBox != null) && intersectsElement(finalPos, finalBox))) return [3 /*break*/, 8];
log("Successfully moved over element: ".concat(selector));
return [2 /*return*/, true];
case 8:
poll = 1;
_f.label = 9;
case 9:
if (!(poll <= 2)) return [3 /*break*/, 14];
return [4 /*yield*/, delay(50 + Math.random() * 100)]; // Human-like pause
case 10:
_f.sent(); // Human-like pause
return [4 /*yield*/, getElementBox(elem)];
case 11:
pollBox = _f.sent();
if ((pollBox != null) && intersectsElement(previous, pollBox)) {
log("Successfully moved over element on poll attempt: ".concat(poll));
return [2 /*return*/, true];
}
if (!(pollBox != null)) return [3 /*break*/, 13];
correctionTarget = getRandomBoxPoint(pollBox, { paddingPercentage: optionsResolved.paddingPercentage });
return [4 /*yield*/, tracePath(path(previous, correctionTarget, { spreadOverride: 5, moveSpeed: ((_e = optionsResolved.moveSpeed) !== null && _e !== void 0 ? _e : 1) * 2 }))];
case 12:
_f.sent();
_f.label = 13;
case 13:
poll++;
return [3 /*break*/, 9];
case 14:
log("Move completed, but cursor at ".concat(JSON.stringify(finalPos), " is not over the element's final box: ").concat(JSON.stringify(finalBox), ". Retrying..."));
lastError = new Error("Cursor position ".concat(JSON.stringify(finalPos), " does not intersect element box ").concat(JSON.stringify(finalBox)));
return [4 /*yield*/, go_1(iteration + 1)];
case 15: return [2 /*return*/, _f.sent()];
case 16: return [3 /*break*/, 20];
case 17:
error_5 = _f.sent();
lastError = error_5 instanceof Error ? error_5 : new Error(String(error_5));
log("Error during move attempt ".concat((iteration + 1).toString(), ":"), lastError.message);
return [4 /*yield*/, delay(200 + Math.random() * 300)];
case 18:
_f.sent();
return [4 /*yield*/, go_1(iteration + 1)];
case 19: return [2 /*return*/, _f.sent()];
case 20: return [2 /*return*/];
}
});
}); };
return [4 /*yield*/, go_1(0)];
case 3:
success = _a.sent();
if (!success && lastError !== null) {
throw lastError;
}
return [3 /*break*/, 6];
case 4: return [4 /*yield*/, _endAction(wasRandom, optionsResolved)];
case 5:
_a.sent();
if (!success) {
log("Move action failed for selector: ".concat(selector));
}
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
});
},
moveTo: function (destination, options) {
return __awaiter(this, void 0, void 0, function () {
var optionsResolved, wasRandom, error_6;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
optionsResolved = __assign(__assign({ moveDelay: 0, randomizeMoveDelay: true, moveSpeed: 1 }, defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.moveTo), options);
return [4 /*yield*/, _startAction()];
case 1:
wasRandom = _a.sent();
_a.label = 2;
case 2:
_a.trys.push([2, 4, 5, 7]);
log("Moving to absolute position: ".concat(JSON.stringify(destination)));
return [4 /*yield*/, tracePath(path(previous, destination, optionsResolved), false)];
case 3:
_a.sent();
return [3 /*break*/, 7];
case 4:
error_6 = _a.sent();
log('Error during moveTo:', error_6);
throw new Error("moveTo failed: ".concat(String(error_6)));
case 5: return [4 /*yield*/, _endAction(wasRandom, optionsResolved)];
case 6:
_a.sent();
return [7 /*endfinally*/];
case 7: return [2 /*return*/];
}
});
});
}
};
if (isPerformingRandomMoves) {
_scheduleRandomMove(defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.randomMove);
}
return actions;
};
exports.createCursor = createCursor;