UNPKG

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
"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;