@hasna/browser 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +400 -335
- package/dist/index.js +8253 -7844
- package/dist/lib/actions.d.ts +1 -0
- package/dist/lib/actions.d.ts.map +1 -1
- package/dist/lib/coordination.d.ts.map +1 -1
- package/dist/lib/network.d.ts.map +1 -1
- package/dist/lib/page-memory.d.ts.map +1 -1
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/task-queue.d.ts.map +1 -1
- package/dist/mcp/index.js +594 -529
- package/dist/server/index.js +1048 -312
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -3,6 +3,7 @@ var __create = Object.create;
|
|
|
3
3
|
var __getProtoOf = Object.getPrototypeOf;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
8
|
function __accessProp(key) {
|
|
8
9
|
return this[key];
|
|
@@ -29,6 +30,23 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
29
30
|
cache.set(mod, to);
|
|
30
31
|
return to;
|
|
31
32
|
};
|
|
33
|
+
var __toCommonJS = (from) => {
|
|
34
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
35
|
+
if (entry)
|
|
36
|
+
return entry;
|
|
37
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
38
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
39
|
+
for (var key of __getOwnPropNames(from))
|
|
40
|
+
if (!__hasOwnProp.call(entry, key))
|
|
41
|
+
__defProp(entry, key, {
|
|
42
|
+
get: __accessProp.bind(from, key),
|
|
43
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
__moduleCache.set(from, entry);
|
|
47
|
+
return entry;
|
|
48
|
+
};
|
|
49
|
+
var __moduleCache;
|
|
32
50
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
51
|
var __returnValue = (v) => v;
|
|
34
52
|
function __exportSetter(name, newValue) {
|
|
@@ -9487,6 +9505,12 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
|
|
|
9487
9505
|
});
|
|
9488
9506
|
|
|
9489
9507
|
// src/db/schema.ts
|
|
9508
|
+
var exports_schema = {};
|
|
9509
|
+
__export(exports_schema, {
|
|
9510
|
+
resetDatabase: () => resetDatabase,
|
|
9511
|
+
getDatabase: () => getDatabase,
|
|
9512
|
+
getDataDir: () => getDataDir2
|
|
9513
|
+
});
|
|
9490
9514
|
import { join as join5 } from "path";
|
|
9491
9515
|
import { mkdirSync as mkdirSync3, existsSync as existsSync4, readdirSync as readdirSync3, copyFileSync as copyFileSync2, statSync } from "fs";
|
|
9492
9516
|
import { homedir as homedir6 } from "os";
|
|
@@ -9531,6 +9555,15 @@ function getDatabase(path) {
|
|
|
9531
9555
|
runMigrations(_db);
|
|
9532
9556
|
return _db;
|
|
9533
9557
|
}
|
|
9558
|
+
function resetDatabase() {
|
|
9559
|
+
if (_db) {
|
|
9560
|
+
try {
|
|
9561
|
+
_db.close();
|
|
9562
|
+
} catch {}
|
|
9563
|
+
}
|
|
9564
|
+
_db = null;
|
|
9565
|
+
_dbPath = null;
|
|
9566
|
+
}
|
|
9534
9567
|
function runMigrations(db) {
|
|
9535
9568
|
db.exec(`
|
|
9536
9569
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
@@ -9884,6 +9917,94 @@ var init_console_log = __esm(() => {
|
|
|
9884
9917
|
init_schema();
|
|
9885
9918
|
});
|
|
9886
9919
|
|
|
9920
|
+
// src/lib/dialogs.ts
|
|
9921
|
+
var exports_dialogs = {};
|
|
9922
|
+
__export(exports_dialogs, {
|
|
9923
|
+
setupDialogHandler: () => setupDialogHandler,
|
|
9924
|
+
handleDialog: () => handleDialog,
|
|
9925
|
+
getDialogs: () => getDialogs,
|
|
9926
|
+
clearDialogs: () => clearDialogs
|
|
9927
|
+
});
|
|
9928
|
+
function setupDialogHandler(page, sessionId) {
|
|
9929
|
+
const onDialog = (dialog) => {
|
|
9930
|
+
const info = {
|
|
9931
|
+
type: dialog.type(),
|
|
9932
|
+
message: dialog.message(),
|
|
9933
|
+
default_value: dialog.defaultValue(),
|
|
9934
|
+
timestamp: new Date().toISOString()
|
|
9935
|
+
};
|
|
9936
|
+
const autoTimer = setTimeout(() => {
|
|
9937
|
+
try {
|
|
9938
|
+
dialog.dismiss().catch(() => {});
|
|
9939
|
+
} catch {}
|
|
9940
|
+
const list = pendingDialogs.get(sessionId);
|
|
9941
|
+
if (list) {
|
|
9942
|
+
const idx = list.findIndex((p) => p.dialog === dialog);
|
|
9943
|
+
if (idx >= 0)
|
|
9944
|
+
list.splice(idx, 1);
|
|
9945
|
+
if (list.length === 0)
|
|
9946
|
+
pendingDialogs.delete(sessionId);
|
|
9947
|
+
}
|
|
9948
|
+
}, AUTO_DISMISS_MS);
|
|
9949
|
+
const pending = { dialog, info, autoTimer };
|
|
9950
|
+
if (!pendingDialogs.has(sessionId)) {
|
|
9951
|
+
pendingDialogs.set(sessionId, []);
|
|
9952
|
+
}
|
|
9953
|
+
pendingDialogs.get(sessionId).push(pending);
|
|
9954
|
+
};
|
|
9955
|
+
page.on("dialog", onDialog);
|
|
9956
|
+
return () => {
|
|
9957
|
+
page.off("dialog", onDialog);
|
|
9958
|
+
const list = pendingDialogs.get(sessionId);
|
|
9959
|
+
if (list) {
|
|
9960
|
+
for (const p of list)
|
|
9961
|
+
clearTimeout(p.autoTimer);
|
|
9962
|
+
pendingDialogs.delete(sessionId);
|
|
9963
|
+
}
|
|
9964
|
+
};
|
|
9965
|
+
}
|
|
9966
|
+
function getDialogs(sessionId) {
|
|
9967
|
+
const list = pendingDialogs.get(sessionId);
|
|
9968
|
+
if (!list)
|
|
9969
|
+
return [];
|
|
9970
|
+
return list.map((p) => p.info);
|
|
9971
|
+
}
|
|
9972
|
+
async function handleDialog(sessionId, action, promptText) {
|
|
9973
|
+
const list = pendingDialogs.get(sessionId);
|
|
9974
|
+
if (!list || list.length === 0) {
|
|
9975
|
+
return { handled: false };
|
|
9976
|
+
}
|
|
9977
|
+
const pending = list.shift();
|
|
9978
|
+
clearTimeout(pending.autoTimer);
|
|
9979
|
+
if (list.length === 0) {
|
|
9980
|
+
pendingDialogs.delete(sessionId);
|
|
9981
|
+
}
|
|
9982
|
+
try {
|
|
9983
|
+
if (action === "accept") {
|
|
9984
|
+
await pending.dialog.accept(promptText);
|
|
9985
|
+
} else {
|
|
9986
|
+
await pending.dialog.dismiss();
|
|
9987
|
+
}
|
|
9988
|
+
} catch {}
|
|
9989
|
+
return { handled: true, dialog: pending.info };
|
|
9990
|
+
}
|
|
9991
|
+
function clearDialogs(sessionId) {
|
|
9992
|
+
const list = pendingDialogs.get(sessionId);
|
|
9993
|
+
if (list) {
|
|
9994
|
+
for (const p of list) {
|
|
9995
|
+
clearTimeout(p.autoTimer);
|
|
9996
|
+
try {
|
|
9997
|
+
p.dialog.dismiss().catch(() => {});
|
|
9998
|
+
} catch {}
|
|
9999
|
+
}
|
|
10000
|
+
pendingDialogs.delete(sessionId);
|
|
10001
|
+
}
|
|
10002
|
+
}
|
|
10003
|
+
var pendingDialogs, AUTO_DISMISS_MS = 5000;
|
|
10004
|
+
var init_dialogs = __esm(() => {
|
|
10005
|
+
pendingDialogs = new Map;
|
|
10006
|
+
});
|
|
10007
|
+
|
|
9887
10008
|
// src/engines/cdp.ts
|
|
9888
10009
|
var exports_cdp = {};
|
|
9889
10010
|
__export(exports_cdp, {
|
|
@@ -10066,150 +10187,904 @@ var init_storage_state = __esm(() => {
|
|
|
10066
10187
|
STATES_DIR = join8(getDataDir2(), "states");
|
|
10067
10188
|
});
|
|
10068
10189
|
|
|
10069
|
-
//
|
|
10070
|
-
var
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
switch (val.constructor) {
|
|
10084
|
-
case Uint8Array:
|
|
10085
|
-
case Uint8ClampedArray:
|
|
10086
|
-
case Int8Array:
|
|
10087
|
-
case Uint16Array:
|
|
10088
|
-
case Int16Array:
|
|
10089
|
-
case Uint32Array:
|
|
10090
|
-
case Int32Array:
|
|
10091
|
-
case Float32Array:
|
|
10092
|
-
case Float64Array:
|
|
10093
|
-
return true;
|
|
10094
|
-
}
|
|
10095
|
-
}
|
|
10096
|
-
return false;
|
|
10097
|
-
};
|
|
10098
|
-
var arrayBuffer = (val) => val instanceof ArrayBuffer;
|
|
10099
|
-
var string = (val) => typeof val === "string" && val.length > 0;
|
|
10100
|
-
var number = (val) => typeof val === "number" && !Number.isNaN(val);
|
|
10101
|
-
var integer = (val) => Number.isInteger(val);
|
|
10102
|
-
var inRange = (val, min, max) => val >= min && val <= max;
|
|
10103
|
-
var inArray = (val, list) => list.includes(val);
|
|
10104
|
-
var invalidParameterError = (name, expected, actual) => new Error(`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`);
|
|
10105
|
-
var nativeError = (native, context) => {
|
|
10106
|
-
context.message = native.message;
|
|
10107
|
-
return context;
|
|
10108
|
-
};
|
|
10109
|
-
module.exports = {
|
|
10110
|
-
defined,
|
|
10111
|
-
object,
|
|
10112
|
-
plainObject,
|
|
10113
|
-
fn,
|
|
10114
|
-
bool,
|
|
10115
|
-
buffer,
|
|
10116
|
-
typedArray,
|
|
10117
|
-
arrayBuffer,
|
|
10118
|
-
string,
|
|
10119
|
-
number,
|
|
10120
|
-
integer,
|
|
10121
|
-
inRange,
|
|
10122
|
-
inArray,
|
|
10123
|
-
invalidParameterError,
|
|
10124
|
-
nativeError
|
|
10125
|
-
};
|
|
10190
|
+
// src/lib/snapshot.ts
|
|
10191
|
+
var exports_snapshot = {};
|
|
10192
|
+
__export(exports_snapshot, {
|
|
10193
|
+
takeSnapshot: () => takeSnapshot,
|
|
10194
|
+
takeBunSnapshot: () => takeBunSnapshot,
|
|
10195
|
+
setLastSnapshot: () => setLastSnapshot,
|
|
10196
|
+
hasRefs: () => hasRefs,
|
|
10197
|
+
getSessionRefs: () => getSessionRefs,
|
|
10198
|
+
getRefLocator: () => getRefLocator,
|
|
10199
|
+
getRefInfo: () => getRefInfo,
|
|
10200
|
+
getLastSnapshot: () => getLastSnapshot,
|
|
10201
|
+
diffSnapshots: () => diffSnapshots,
|
|
10202
|
+
clearSessionRefs: () => clearSessionRefs,
|
|
10203
|
+
clearLastSnapshot: () => clearLastSnapshot
|
|
10126
10204
|
});
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10205
|
+
function getLastSnapshot(sessionId) {
|
|
10206
|
+
return lastSnapshots.get(sessionId) ?? null;
|
|
10207
|
+
}
|
|
10208
|
+
function setLastSnapshot(sessionId, snapshot) {
|
|
10209
|
+
lastSnapshots.set(sessionId, snapshot);
|
|
10210
|
+
}
|
|
10211
|
+
function clearLastSnapshot(sessionId) {
|
|
10212
|
+
lastSnapshots.delete(sessionId);
|
|
10213
|
+
}
|
|
10214
|
+
async function takeSnapshot(page, sessionId) {
|
|
10215
|
+
const isBunView = typeof page.getNativeView === "function" || typeof page.bunView !== "undefined";
|
|
10216
|
+
if (isBunView) {
|
|
10217
|
+
return takeBunSnapshot(page, sessionId);
|
|
10218
|
+
}
|
|
10219
|
+
let ariaTree;
|
|
10220
|
+
try {
|
|
10221
|
+
ariaTree = await page.locator("body").ariaSnapshot();
|
|
10222
|
+
} catch {
|
|
10223
|
+
ariaTree = "";
|
|
10224
|
+
}
|
|
10225
|
+
const refs = {};
|
|
10226
|
+
const refMap = new Map;
|
|
10227
|
+
let refCounter = 0;
|
|
10228
|
+
for (const role of INTERACTIVE_ROLES) {
|
|
10229
|
+
const locators = page.getByRole(role);
|
|
10230
|
+
const count = await locators.count();
|
|
10231
|
+
for (let i = 0;i < count; i++) {
|
|
10232
|
+
const el = locators.nth(i);
|
|
10233
|
+
let name = "";
|
|
10234
|
+
let visible = false;
|
|
10235
|
+
let enabled = true;
|
|
10236
|
+
let value;
|
|
10237
|
+
let checked;
|
|
10238
|
+
try {
|
|
10239
|
+
visible = await el.isVisible();
|
|
10240
|
+
if (!visible)
|
|
10241
|
+
continue;
|
|
10242
|
+
} catch {
|
|
10243
|
+
continue;
|
|
10244
|
+
}
|
|
10245
|
+
try {
|
|
10246
|
+
name = await el.evaluate((e) => {
|
|
10247
|
+
const el2 = e;
|
|
10248
|
+
return el2.getAttribute("aria-label") ?? el2.textContent?.trim().slice(0, 80) ?? el2.getAttribute("title") ?? el2.getAttribute("placeholder") ?? "";
|
|
10249
|
+
});
|
|
10250
|
+
} catch {
|
|
10251
|
+
continue;
|
|
10141
10252
|
}
|
|
10253
|
+
if (!name)
|
|
10254
|
+
continue;
|
|
10255
|
+
try {
|
|
10256
|
+
enabled = await el.isEnabled();
|
|
10257
|
+
} catch {}
|
|
10258
|
+
try {
|
|
10259
|
+
if (role === "checkbox" || role === "radio" || role === "switch") {
|
|
10260
|
+
checked = await el.isChecked();
|
|
10261
|
+
}
|
|
10262
|
+
} catch {}
|
|
10263
|
+
try {
|
|
10264
|
+
if (role === "textbox" || role === "searchbox" || role === "spinbutton" || role === "combobox") {
|
|
10265
|
+
value = await el.inputValue();
|
|
10266
|
+
}
|
|
10267
|
+
} catch {}
|
|
10268
|
+
const ref = `@e${refCounter}`;
|
|
10269
|
+
refCounter++;
|
|
10270
|
+
refs[ref] = { role, name, visible, enabled, value, checked };
|
|
10271
|
+
const escapedName = name.replace(/"/g, "\\\"");
|
|
10272
|
+
refMap.set(ref, { role, name, locatorSelector: `role=${role}[name="${escapedName}"]` });
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
let annotatedTree = ariaTree;
|
|
10276
|
+
for (const [ref, info] of Object.entries(refs)) {
|
|
10277
|
+
const escapedName = info.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10278
|
+
const pattern = new RegExp(`(${info.role}\\s+"${escapedName.slice(0, 40)}[^"]*")`, "m");
|
|
10279
|
+
const match = annotatedTree.match(pattern);
|
|
10280
|
+
if (match) {
|
|
10281
|
+
annotatedTree = annotatedTree.replace(match[0], `${match[0]} [${ref}]`);
|
|
10142
10282
|
}
|
|
10143
|
-
|
|
10144
|
-
};
|
|
10145
|
-
|
|
10146
|
-
|
|
10283
|
+
}
|
|
10284
|
+
const unmatchedRefs = Object.entries(refs).filter(([ref]) => !annotatedTree.includes(`[${ref}]`));
|
|
10285
|
+
if (unmatchedRefs.length > 0) {
|
|
10286
|
+
annotatedTree += `
|
|
10147
10287
|
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10288
|
+
--- Interactive elements ---`;
|
|
10289
|
+
for (const [ref, info] of unmatchedRefs) {
|
|
10290
|
+
const extras = [];
|
|
10291
|
+
if (info.checked !== undefined)
|
|
10292
|
+
extras.push(`checked=${info.checked}`);
|
|
10293
|
+
if (!info.enabled)
|
|
10294
|
+
extras.push("disabled");
|
|
10295
|
+
if (info.value)
|
|
10296
|
+
extras.push(`value="${info.value}"`);
|
|
10297
|
+
const extrasStr = extras.length ? ` (${extras.join(", ")})` : "";
|
|
10298
|
+
annotatedTree += `
|
|
10299
|
+
${info.role} "${info.name}" [${ref}]${extrasStr}`;
|
|
10300
|
+
}
|
|
10301
|
+
}
|
|
10302
|
+
if (sessionId) {
|
|
10303
|
+
sessionRefMaps.set(sessionId, refMap);
|
|
10304
|
+
}
|
|
10305
|
+
return {
|
|
10306
|
+
tree: annotatedTree,
|
|
10307
|
+
refs,
|
|
10308
|
+
interactive_count: refCounter
|
|
10160
10309
|
};
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10310
|
+
}
|
|
10311
|
+
function getRefLocator(page, sessionId, ref) {
|
|
10312
|
+
const refMap = sessionRefMaps.get(sessionId);
|
|
10313
|
+
if (!refMap)
|
|
10314
|
+
throw new Error(`No snapshot taken for session ${sessionId}. Call browser_snapshot first.`);
|
|
10315
|
+
const entry = refMap.get(ref);
|
|
10316
|
+
if (!entry)
|
|
10317
|
+
throw new Error(`Ref ${ref} not found. Available refs: ${[...refMap.keys()].slice(0, 20).join(", ")}`);
|
|
10318
|
+
return page.getByRole(entry.role, { name: entry.name }).first();
|
|
10319
|
+
}
|
|
10320
|
+
function getRefInfo(sessionId, ref) {
|
|
10321
|
+
const refMap = sessionRefMaps.get(sessionId);
|
|
10322
|
+
if (!refMap)
|
|
10323
|
+
return null;
|
|
10324
|
+
return refMap.get(ref) ?? null;
|
|
10325
|
+
}
|
|
10326
|
+
function getSessionRefs(sessionId) {
|
|
10327
|
+
return sessionRefMaps.get(sessionId) ?? null;
|
|
10328
|
+
}
|
|
10329
|
+
function clearSessionRefs(sessionId) {
|
|
10330
|
+
sessionRefMaps.delete(sessionId);
|
|
10331
|
+
}
|
|
10332
|
+
function hasRefs(sessionId) {
|
|
10333
|
+
return sessionRefMaps.has(sessionId) && (sessionRefMaps.get(sessionId)?.size ?? 0) > 0;
|
|
10334
|
+
}
|
|
10335
|
+
function refKey(info) {
|
|
10336
|
+
return `${info.role}::${info.name}`;
|
|
10337
|
+
}
|
|
10338
|
+
function diffSnapshots(before, after) {
|
|
10339
|
+
const beforeMap = new Map;
|
|
10340
|
+
for (const [ref, info] of Object.entries(before.refs)) {
|
|
10341
|
+
beforeMap.set(refKey(info), { ref, info });
|
|
10342
|
+
}
|
|
10343
|
+
const afterMap = new Map;
|
|
10344
|
+
for (const [ref, info] of Object.entries(after.refs)) {
|
|
10345
|
+
afterMap.set(refKey(info), { ref, info });
|
|
10346
|
+
}
|
|
10347
|
+
const added = [];
|
|
10348
|
+
const removed = [];
|
|
10349
|
+
const modified = [];
|
|
10350
|
+
for (const [key, afterEntry] of afterMap) {
|
|
10351
|
+
const beforeEntry = beforeMap.get(key);
|
|
10352
|
+
if (!beforeEntry) {
|
|
10353
|
+
added.push({ ref: afterEntry.ref, info: afterEntry.info });
|
|
10354
|
+
} else {
|
|
10355
|
+
const b = beforeEntry.info;
|
|
10356
|
+
const a = afterEntry.info;
|
|
10357
|
+
if (b.visible !== a.visible || b.enabled !== a.enabled || b.value !== a.value || b.checked !== a.checked || b.description !== a.description) {
|
|
10358
|
+
modified.push({ ref: afterEntry.ref, before: b, after: a });
|
|
10171
10359
|
}
|
|
10172
|
-
});
|
|
10173
|
-
});
|
|
10174
|
-
module.exports = {
|
|
10175
|
-
LDD_PATH,
|
|
10176
|
-
SELF_PATH,
|
|
10177
|
-
readFileSync: readFileSync2,
|
|
10178
|
-
readFile
|
|
10179
|
-
};
|
|
10180
|
-
});
|
|
10181
|
-
|
|
10182
|
-
// node_modules/detect-libc/lib/elf.js
|
|
10183
|
-
var require_elf = __commonJS((exports, module) => {
|
|
10184
|
-
var interpreterPath = (elf) => {
|
|
10185
|
-
if (elf.length < 64) {
|
|
10186
|
-
return null;
|
|
10187
|
-
}
|
|
10188
|
-
if (elf.readUInt32BE(0) !== 2135247942) {
|
|
10189
|
-
return null;
|
|
10190
|
-
}
|
|
10191
|
-
if (elf.readUInt8(4) !== 2) {
|
|
10192
|
-
return null;
|
|
10193
10360
|
}
|
|
10194
|
-
|
|
10195
|
-
|
|
10361
|
+
}
|
|
10362
|
+
for (const [key, beforeEntry] of beforeMap) {
|
|
10363
|
+
if (!afterMap.has(key)) {
|
|
10364
|
+
removed.push({ ref: beforeEntry.ref, info: beforeEntry.info });
|
|
10196
10365
|
}
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10366
|
+
}
|
|
10367
|
+
const url_changed = before.tree.split(`
|
|
10368
|
+
`)[0] !== after.tree.split(`
|
|
10369
|
+
`)[0];
|
|
10370
|
+
const title_changed = before.tree !== after.tree && (added.length > 0 || removed.length > 0 || modified.length > 0);
|
|
10371
|
+
return { added, removed, modified, url_changed, title_changed };
|
|
10372
|
+
}
|
|
10373
|
+
async function takeBunSnapshot(page, sessionId) {
|
|
10374
|
+
const refs = {};
|
|
10375
|
+
const refMap = new Map;
|
|
10376
|
+
let refCounter = 0;
|
|
10377
|
+
const lines = [];
|
|
10378
|
+
try {
|
|
10379
|
+
const elements = await page.evaluate(`
|
|
10380
|
+
(() => {
|
|
10381
|
+
const SELECTOR = 'a[href], button, input:not([type=hidden]), select, textarea, [role=button], [role=link], [role=checkbox], [role=combobox], [role=menuitem], [role=tab], [role=option]';
|
|
10382
|
+
const els = Array.from(document.querySelectorAll(SELECTOR));
|
|
10383
|
+
return els.slice(0, 100).map(el => {
|
|
10384
|
+
const tag = el.tagName.toLowerCase();
|
|
10385
|
+
const inputType = el.getAttribute('type') ?? '';
|
|
10386
|
+
let role = el.getAttribute('role') || (['a'].includes(tag) ? 'link' : ['button'].includes(tag) ? 'button' : ['input'].includes(tag) ? (inputType === 'checkbox' ? 'checkbox' : inputType === 'radio' ? 'radio' : 'textbox') : ['select'].includes(tag) ? 'combobox' : ['textarea'].includes(tag) ? 'textbox' : tag);
|
|
10387
|
+
const name = (el.getAttribute('aria-label') || el.textContent?.trim() || el.getAttribute('placeholder') || el.getAttribute('title') || el.getAttribute('value') || el.id || '').slice(0, 80);
|
|
10388
|
+
const enabled = !el.disabled && !el.getAttribute('disabled');
|
|
10389
|
+
const style = window.getComputedStyle(el);
|
|
10390
|
+
const visible = style.display !== 'none' && style.visibility !== 'hidden' && el.offsetWidth > 0;
|
|
10391
|
+
const checked = el.type === 'checkbox' || el.type === 'radio' ? el.checked : undefined;
|
|
10392
|
+
const value = ['input', 'select', 'textarea'].includes(tag) && el.type !== 'checkbox' && el.type !== 'radio' ? el.value : undefined;
|
|
10393
|
+
const selector = el.id ? '#' + el.id : (el.getAttribute('aria-label') ? '[aria-label="' + el.getAttribute('aria-label') + '"]' : tag);
|
|
10394
|
+
return { role, name, enabled, visible, checked, value, selector };
|
|
10395
|
+
}).filter(e => e.visible && e.name);
|
|
10396
|
+
})()
|
|
10397
|
+
`);
|
|
10398
|
+
const pageTitle = await page.evaluate("document.title");
|
|
10399
|
+
const pageUrl = typeof page.url === "function" ? page.url() : "";
|
|
10400
|
+
lines.push(`# ${pageTitle || "Page"} (${pageUrl})`);
|
|
10401
|
+
for (const el of elements) {
|
|
10402
|
+
if (!el.name)
|
|
10403
|
+
continue;
|
|
10404
|
+
const ref = `@e${refCounter}`;
|
|
10405
|
+
refCounter++;
|
|
10406
|
+
refs[ref] = {
|
|
10407
|
+
role: el.role,
|
|
10408
|
+
name: el.name,
|
|
10409
|
+
visible: el.visible,
|
|
10410
|
+
enabled: el.enabled,
|
|
10411
|
+
value: el.value,
|
|
10412
|
+
checked: el.checked
|
|
10413
|
+
};
|
|
10414
|
+
refMap.set(ref, { role: el.role, name: el.name, locatorSelector: el.selector });
|
|
10415
|
+
const extras = [];
|
|
10416
|
+
if (el.checked !== undefined)
|
|
10417
|
+
extras.push(`checked=${el.checked}`);
|
|
10418
|
+
if (!el.enabled)
|
|
10419
|
+
extras.push("disabled");
|
|
10420
|
+
if (el.value && el.value !== el.name)
|
|
10421
|
+
extras.push(`value="${el.value.slice(0, 30)}"`);
|
|
10422
|
+
const extrasStr = extras.length ? ` (${extras.join(", ")})` : "";
|
|
10423
|
+
lines.push(`${el.role} "${el.name}" [${ref}]${extrasStr}`);
|
|
10208
10424
|
}
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10425
|
+
} catch (err) {
|
|
10426
|
+
lines.push(`# (snapshot error: ${err instanceof Error ? err.message : String(err)})`);
|
|
10427
|
+
}
|
|
10428
|
+
if (sessionId) {
|
|
10429
|
+
sessionRefMaps.set(sessionId, refMap);
|
|
10430
|
+
}
|
|
10431
|
+
return {
|
|
10432
|
+
tree: lines.join(`
|
|
10433
|
+
`),
|
|
10434
|
+
refs,
|
|
10435
|
+
interactive_count: refCounter
|
|
10436
|
+
};
|
|
10437
|
+
}
|
|
10438
|
+
var lastSnapshots, sessionRefMaps, INTERACTIVE_ROLES;
|
|
10439
|
+
var init_snapshot = __esm(() => {
|
|
10440
|
+
lastSnapshots = new Map;
|
|
10441
|
+
sessionRefMaps = new Map;
|
|
10442
|
+
INTERACTIVE_ROLES = [
|
|
10443
|
+
"button",
|
|
10444
|
+
"link",
|
|
10445
|
+
"textbox",
|
|
10446
|
+
"checkbox",
|
|
10447
|
+
"radio",
|
|
10448
|
+
"combobox",
|
|
10449
|
+
"menuitem",
|
|
10450
|
+
"menuitemcheckbox",
|
|
10451
|
+
"menuitemradio",
|
|
10452
|
+
"option",
|
|
10453
|
+
"searchbox",
|
|
10454
|
+
"slider",
|
|
10455
|
+
"spinbutton",
|
|
10456
|
+
"switch",
|
|
10457
|
+
"tab",
|
|
10458
|
+
"treeitem",
|
|
10459
|
+
"listbox",
|
|
10460
|
+
"menu"
|
|
10461
|
+
];
|
|
10462
|
+
});
|
|
10463
|
+
|
|
10464
|
+
// src/lib/self-heal.ts
|
|
10465
|
+
async function healSelector(page, selector, sessionId) {
|
|
10466
|
+
const attempts = [];
|
|
10467
|
+
attempts.push(`selector: ${selector}`);
|
|
10468
|
+
try {
|
|
10469
|
+
const loc = page.locator(selector).first();
|
|
10470
|
+
if (await loc.count() > 0) {
|
|
10471
|
+
return { found: true, locator: loc, method: "original", healed: false, attempts };
|
|
10472
|
+
}
|
|
10473
|
+
} catch {}
|
|
10474
|
+
if (!selector.startsWith("#") && !selector.startsWith(".") && !selector.startsWith("[") && !selector.includes(">") && !selector.includes(" ")) {
|
|
10475
|
+
attempts.push(`text: "${selector}"`);
|
|
10476
|
+
try {
|
|
10477
|
+
const loc = page.getByText(selector, { exact: false }).first();
|
|
10478
|
+
if (await loc.count() > 0) {
|
|
10479
|
+
return { found: true, locator: loc, method: "text", healed: true, attempts };
|
|
10480
|
+
}
|
|
10481
|
+
} catch {}
|
|
10482
|
+
}
|
|
10483
|
+
const roleMap = {
|
|
10484
|
+
button: ["button", "submit", "reset"],
|
|
10485
|
+
link: ["a"],
|
|
10486
|
+
input: ["input", "textarea"],
|
|
10487
|
+
heading: ["h1", "h2", "h3", "h4", "h5", "h6"]
|
|
10488
|
+
};
|
|
10489
|
+
const nameHint = selector.replace(/^[#.]/, "").replace(/[-_]/g, " ").toLowerCase();
|
|
10490
|
+
for (const [role, tags] of Object.entries(roleMap)) {
|
|
10491
|
+
attempts.push(`role: ${role} name~="${nameHint}"`);
|
|
10492
|
+
try {
|
|
10493
|
+
const loc = page.getByRole(role, { name: new RegExp(nameHint.split(" ")[0], "i") }).first();
|
|
10494
|
+
if (await loc.count() > 0) {
|
|
10495
|
+
return { found: true, locator: loc, method: "role", healed: true, attempts };
|
|
10496
|
+
}
|
|
10497
|
+
} catch {}
|
|
10498
|
+
}
|
|
10499
|
+
if (selector.startsWith("#")) {
|
|
10500
|
+
const idPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
|
|
10501
|
+
const partialSel = `[id*="${idPart}"]`;
|
|
10502
|
+
attempts.push(`partial_id: ${partialSel}`);
|
|
10503
|
+
try {
|
|
10504
|
+
const loc = page.locator(partialSel).first();
|
|
10505
|
+
if (await loc.count() > 0) {
|
|
10506
|
+
return { found: true, locator: loc, method: "partial_id", healed: true, attempts };
|
|
10507
|
+
}
|
|
10508
|
+
} catch {}
|
|
10509
|
+
}
|
|
10510
|
+
if (selector.startsWith(".")) {
|
|
10511
|
+
const classPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
|
|
10512
|
+
const partialSel = `[class*="${classPart}"]`;
|
|
10513
|
+
attempts.push(`partial_class: ${partialSel}`);
|
|
10514
|
+
try {
|
|
10515
|
+
const loc = page.locator(partialSel).first();
|
|
10516
|
+
if (await loc.count() > 0) {
|
|
10517
|
+
return { found: true, locator: loc, method: "partial_class", healed: true, attempts };
|
|
10518
|
+
}
|
|
10519
|
+
} catch {}
|
|
10520
|
+
}
|
|
10521
|
+
return { found: false, locator: null, method: "none", healed: false, attempts };
|
|
10522
|
+
}
|
|
10523
|
+
|
|
10524
|
+
// src/lib/actions.ts
|
|
10525
|
+
var exports_actions = {};
|
|
10526
|
+
__export(exports_actions, {
|
|
10527
|
+
withRetry: () => withRetry,
|
|
10528
|
+
watchPage: () => watchPage,
|
|
10529
|
+
waitForText: () => waitForText,
|
|
10530
|
+
waitForSelector: () => waitForSelector,
|
|
10531
|
+
waitForNavigation: () => waitForNavigation,
|
|
10532
|
+
uploadFile: () => uploadFile,
|
|
10533
|
+
typeRef: () => typeRef,
|
|
10534
|
+
type: () => type,
|
|
10535
|
+
stopWatch: () => stopWatch,
|
|
10536
|
+
stopAllWatchesForSession: () => stopAllWatchesForSession,
|
|
10537
|
+
selectRef: () => selectRef,
|
|
10538
|
+
selectOption: () => selectOption,
|
|
10539
|
+
scrollTo: () => scrollTo,
|
|
10540
|
+
scroll: () => scroll,
|
|
10541
|
+
reload: () => reload,
|
|
10542
|
+
pressKey: () => pressKey,
|
|
10543
|
+
navigate: () => navigate,
|
|
10544
|
+
hoverRef: () => hoverRef,
|
|
10545
|
+
hover: () => hover,
|
|
10546
|
+
goForward: () => goForward,
|
|
10547
|
+
goBack: () => goBack,
|
|
10548
|
+
getWatchChanges: () => getWatchChanges,
|
|
10549
|
+
fillRef: () => fillRef,
|
|
10550
|
+
fillForm: () => fillForm,
|
|
10551
|
+
fill: () => fill,
|
|
10552
|
+
clickText: () => clickText,
|
|
10553
|
+
clickRef: () => clickRef,
|
|
10554
|
+
click: () => click,
|
|
10555
|
+
checkRef: () => checkRef,
|
|
10556
|
+
checkBox: () => checkBox
|
|
10557
|
+
});
|
|
10558
|
+
async function click(page, selector, opts) {
|
|
10559
|
+
try {
|
|
10560
|
+
await page.click(selector, {
|
|
10561
|
+
button: opts?.button ?? "left",
|
|
10562
|
+
clickCount: opts?.clickCount ?? 1,
|
|
10563
|
+
delay: opts?.delay,
|
|
10564
|
+
timeout: opts?.timeout ?? 1e4
|
|
10565
|
+
});
|
|
10566
|
+
return {};
|
|
10567
|
+
} catch (originalError) {
|
|
10568
|
+
if (opts?.selfHeal !== false) {
|
|
10569
|
+
const result = await healSelector(page, selector);
|
|
10570
|
+
if (result.found && result.locator) {
|
|
10571
|
+
await result.locator.click({
|
|
10572
|
+
button: opts?.button ?? "left",
|
|
10573
|
+
timeout: opts?.timeout ?? 1e4
|
|
10574
|
+
});
|
|
10575
|
+
return { healed: true, method: result.method, attempts: result.attempts };
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
if (originalError instanceof Error && originalError.message.includes("not found")) {
|
|
10579
|
+
throw new ElementNotFoundError(selector);
|
|
10580
|
+
}
|
|
10581
|
+
throw new BrowserError(`Click failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "CLICK_FAILED");
|
|
10582
|
+
}
|
|
10583
|
+
}
|
|
10584
|
+
async function type(page, selector, text, opts) {
|
|
10585
|
+
try {
|
|
10586
|
+
if (opts?.clear) {
|
|
10587
|
+
await page.fill(selector, "", { timeout: opts?.timeout ?? 1e4 });
|
|
10588
|
+
}
|
|
10589
|
+
await page.type(selector, text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
|
|
10590
|
+
return {};
|
|
10591
|
+
} catch (originalError) {
|
|
10592
|
+
if (opts?.selfHeal !== false) {
|
|
10593
|
+
const result = await healSelector(page, selector);
|
|
10594
|
+
if (result.found && result.locator) {
|
|
10595
|
+
if (opts?.clear)
|
|
10596
|
+
await result.locator.fill("", { timeout: opts?.timeout ?? 1e4 });
|
|
10597
|
+
await result.locator.pressSequentially(text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
|
|
10598
|
+
return { healed: true, method: result.method, attempts: result.attempts };
|
|
10599
|
+
}
|
|
10600
|
+
}
|
|
10601
|
+
if (originalError instanceof Error && originalError.message.includes("not found")) {
|
|
10602
|
+
throw new ElementNotFoundError(selector);
|
|
10603
|
+
}
|
|
10604
|
+
throw new BrowserError(`Type failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "TYPE_FAILED");
|
|
10605
|
+
}
|
|
10606
|
+
}
|
|
10607
|
+
async function fill(page, selector, value, timeout = 1e4, selfHeal = true) {
|
|
10608
|
+
try {
|
|
10609
|
+
await page.fill(selector, value, { timeout });
|
|
10610
|
+
return {};
|
|
10611
|
+
} catch (originalError) {
|
|
10612
|
+
if (selfHeal) {
|
|
10613
|
+
const result = await healSelector(page, selector);
|
|
10614
|
+
if (result.found && result.locator) {
|
|
10615
|
+
await result.locator.fill(value, { timeout });
|
|
10616
|
+
return { healed: true, method: result.method, attempts: result.attempts };
|
|
10617
|
+
}
|
|
10618
|
+
}
|
|
10619
|
+
throw new ElementNotFoundError(selector);
|
|
10620
|
+
}
|
|
10621
|
+
}
|
|
10622
|
+
async function scroll(page, direction = "down", amount = 300) {
|
|
10623
|
+
const x = direction === "left" ? -amount : direction === "right" ? amount : 0;
|
|
10624
|
+
const y = direction === "up" ? -amount : direction === "down" ? amount : 0;
|
|
10625
|
+
await page.evaluate(({ x: x2, y: y2 }) => window.scrollBy(x2, y2), { x, y });
|
|
10626
|
+
}
|
|
10627
|
+
async function scrollTo(page, selector) {
|
|
10628
|
+
try {
|
|
10629
|
+
await page.locator(selector).scrollIntoViewIfNeeded();
|
|
10630
|
+
} catch (err) {
|
|
10631
|
+
throw new ElementNotFoundError(selector);
|
|
10632
|
+
}
|
|
10633
|
+
}
|
|
10634
|
+
async function hover(page, selector, timeout = 1e4) {
|
|
10635
|
+
try {
|
|
10636
|
+
await page.hover(selector, { timeout });
|
|
10637
|
+
} catch (err) {
|
|
10638
|
+
throw new ElementNotFoundError(selector);
|
|
10639
|
+
}
|
|
10640
|
+
}
|
|
10641
|
+
async function selectOption(page, selector, value, timeout = 1e4) {
|
|
10642
|
+
try {
|
|
10643
|
+
return await page.selectOption(selector, value, { timeout });
|
|
10644
|
+
} catch (err) {
|
|
10645
|
+
throw new ElementNotFoundError(selector);
|
|
10646
|
+
}
|
|
10647
|
+
}
|
|
10648
|
+
async function checkBox(page, selector, checked, timeout = 1e4) {
|
|
10649
|
+
try {
|
|
10650
|
+
if (checked) {
|
|
10651
|
+
await page.check(selector, { timeout });
|
|
10652
|
+
} else {
|
|
10653
|
+
await page.uncheck(selector, { timeout });
|
|
10654
|
+
}
|
|
10655
|
+
} catch (err) {
|
|
10656
|
+
throw new ElementNotFoundError(selector);
|
|
10657
|
+
}
|
|
10658
|
+
}
|
|
10659
|
+
async function uploadFile(page, selector, filePaths, timeout = 1e4) {
|
|
10660
|
+
try {
|
|
10661
|
+
await page.setInputFiles(selector, filePaths, { timeout });
|
|
10662
|
+
} catch (err) {
|
|
10663
|
+
throw new ElementNotFoundError(selector);
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
async function goBack(page, timeout = 1e4) {
|
|
10667
|
+
try {
|
|
10668
|
+
await page.goBack({ timeout, waitUntil: "domcontentloaded" });
|
|
10669
|
+
} catch (err) {
|
|
10670
|
+
throw new NavigationError("back", err instanceof Error ? err.message : String(err));
|
|
10671
|
+
}
|
|
10672
|
+
}
|
|
10673
|
+
async function goForward(page, timeout = 1e4) {
|
|
10674
|
+
try {
|
|
10675
|
+
await page.goForward({ timeout, waitUntil: "domcontentloaded" });
|
|
10676
|
+
} catch (err) {
|
|
10677
|
+
throw new NavigationError("forward", err instanceof Error ? err.message : String(err));
|
|
10678
|
+
}
|
|
10679
|
+
}
|
|
10680
|
+
async function reload(page, timeout = 1e4) {
|
|
10681
|
+
try {
|
|
10682
|
+
await page.reload({ timeout, waitUntil: "domcontentloaded" });
|
|
10683
|
+
} catch (err) {
|
|
10684
|
+
throw new NavigationError("reload", err instanceof Error ? err.message : String(err));
|
|
10685
|
+
}
|
|
10686
|
+
}
|
|
10687
|
+
async function navigate(page, url, timeout = 30000) {
|
|
10688
|
+
try {
|
|
10689
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout });
|
|
10690
|
+
} catch (err) {
|
|
10691
|
+
throw new NavigationError(url, err instanceof Error ? err.message : String(err));
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
async function waitForSelector(page, selector, opts) {
|
|
10695
|
+
try {
|
|
10696
|
+
await page.waitForSelector(selector, {
|
|
10697
|
+
state: opts?.state ?? "visible",
|
|
10698
|
+
timeout: opts?.timeout ?? 1e4
|
|
10699
|
+
});
|
|
10700
|
+
} catch (err) {
|
|
10701
|
+
throw new ElementNotFoundError(selector);
|
|
10702
|
+
}
|
|
10703
|
+
}
|
|
10704
|
+
async function waitForNavigation(page, timeout = 30000) {
|
|
10705
|
+
try {
|
|
10706
|
+
await page.waitForLoadState("domcontentloaded", { timeout });
|
|
10707
|
+
} catch (err) {
|
|
10708
|
+
throw new NavigationError("navigation", err instanceof Error ? err.message : String(err));
|
|
10709
|
+
}
|
|
10710
|
+
}
|
|
10711
|
+
async function pressKey(page, key) {
|
|
10712
|
+
await page.keyboard.press(key);
|
|
10713
|
+
}
|
|
10714
|
+
async function withRetry(fn, opts) {
|
|
10715
|
+
const retries = opts?.retries ?? 2;
|
|
10716
|
+
const delay = opts?.delay ?? 300;
|
|
10717
|
+
const retryOn = opts?.retryOn ?? RETRYABLE_ERRORS;
|
|
10718
|
+
let lastErr;
|
|
10719
|
+
for (let attempt = 0;attempt <= retries; attempt++) {
|
|
10720
|
+
try {
|
|
10721
|
+
return await fn();
|
|
10722
|
+
} catch (err) {
|
|
10723
|
+
lastErr = err;
|
|
10724
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10725
|
+
const shouldRetry = retryOn.some((pattern) => msg.includes(pattern));
|
|
10726
|
+
if (!shouldRetry || err instanceof ElementNotFoundError)
|
|
10727
|
+
throw err;
|
|
10728
|
+
if (attempt < retries)
|
|
10729
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
throw lastErr;
|
|
10733
|
+
}
|
|
10734
|
+
async function clickText(page, text, opts) {
|
|
10735
|
+
await withRetry(async () => {
|
|
10736
|
+
try {
|
|
10737
|
+
await page.getByText(text, { exact: opts?.exact ?? false }).first().click({ timeout: opts?.timeout ?? 1e4 });
|
|
10738
|
+
} catch (err) {
|
|
10739
|
+
throw new BrowserError(`clickText: could not find or click text "${text}": ${err instanceof Error ? err.message : String(err)}`, "CLICK_TEXT_FAILED");
|
|
10740
|
+
}
|
|
10741
|
+
}, { retries: opts?.retries ?? 1 });
|
|
10742
|
+
}
|
|
10743
|
+
async function fillForm(page, fields, submitSelector, selfHeal = true) {
|
|
10744
|
+
let filled = 0;
|
|
10745
|
+
const errors = [];
|
|
10746
|
+
const healedFields = [];
|
|
10747
|
+
for (const [selector, value] of Object.entries(fields)) {
|
|
10748
|
+
try {
|
|
10749
|
+
let el = await page.$(selector);
|
|
10750
|
+
if (!el && selfHeal) {
|
|
10751
|
+
const result = await healSelector(page, selector);
|
|
10752
|
+
if (result.found && result.locator) {
|
|
10753
|
+
const handle = await result.locator.elementHandle();
|
|
10754
|
+
if (handle) {
|
|
10755
|
+
el = handle;
|
|
10756
|
+
healedFields.push(selector);
|
|
10757
|
+
const tagName2 = await result.locator.evaluate((e) => e.tagName.toLowerCase());
|
|
10758
|
+
const inputType2 = await result.locator.evaluate((e) => e.type?.toLowerCase() ?? "text");
|
|
10759
|
+
if (tagName2 === "select") {
|
|
10760
|
+
await result.locator.selectOption(String(value));
|
|
10761
|
+
} else if (tagName2 === "input" && (inputType2 === "checkbox" || inputType2 === "radio")) {
|
|
10762
|
+
if (Boolean(value))
|
|
10763
|
+
await result.locator.check();
|
|
10764
|
+
else
|
|
10765
|
+
await result.locator.uncheck();
|
|
10766
|
+
} else {
|
|
10767
|
+
await result.locator.fill(String(value));
|
|
10768
|
+
}
|
|
10769
|
+
filled++;
|
|
10770
|
+
continue;
|
|
10771
|
+
}
|
|
10772
|
+
}
|
|
10773
|
+
errors.push(`${selector}: element not found`);
|
|
10774
|
+
continue;
|
|
10775
|
+
}
|
|
10776
|
+
if (!el) {
|
|
10777
|
+
errors.push(`${selector}: element not found`);
|
|
10778
|
+
continue;
|
|
10779
|
+
}
|
|
10780
|
+
const tagName = await el.evaluate((e) => e.tagName.toLowerCase());
|
|
10781
|
+
const inputType = await el.evaluate((e) => e.type?.toLowerCase() ?? "text");
|
|
10782
|
+
if (tagName === "select") {
|
|
10783
|
+
await page.selectOption(selector, String(value));
|
|
10784
|
+
} else if (tagName === "input" && (inputType === "checkbox" || inputType === "radio")) {
|
|
10785
|
+
const checked = Boolean(value);
|
|
10786
|
+
if (checked) {
|
|
10787
|
+
await page.check(selector);
|
|
10788
|
+
} else {
|
|
10789
|
+
await page.uncheck(selector);
|
|
10790
|
+
}
|
|
10791
|
+
} else {
|
|
10792
|
+
await page.fill(selector, String(value));
|
|
10793
|
+
}
|
|
10794
|
+
filled++;
|
|
10795
|
+
} catch (err) {
|
|
10796
|
+
errors.push(`${selector}: ${err instanceof Error ? err.message : String(err)}`);
|
|
10797
|
+
}
|
|
10798
|
+
}
|
|
10799
|
+
if (submitSelector) {
|
|
10800
|
+
try {
|
|
10801
|
+
await page.click(submitSelector);
|
|
10802
|
+
} catch (submitErr) {
|
|
10803
|
+
if (selfHeal) {
|
|
10804
|
+
const result = await healSelector(page, submitSelector);
|
|
10805
|
+
if (result.found && result.locator) {
|
|
10806
|
+
await result.locator.click();
|
|
10807
|
+
healedFields.push(submitSelector);
|
|
10808
|
+
} else {
|
|
10809
|
+
errors.push(`submit(${submitSelector}): ${submitErr instanceof Error ? submitErr.message : String(submitErr)}`);
|
|
10810
|
+
}
|
|
10811
|
+
} else {
|
|
10812
|
+
errors.push(`submit(${submitSelector}): ${submitErr instanceof Error ? submitErr.message : String(submitErr)}`);
|
|
10813
|
+
}
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
return { filled, errors, fields_attempted: Object.keys(fields).length, ...healedFields.length > 0 ? { healed_fields: healedFields } : {} };
|
|
10817
|
+
}
|
|
10818
|
+
async function waitForText(page, text, opts) {
|
|
10819
|
+
const timeout = opts?.timeout ?? 1e4;
|
|
10820
|
+
try {
|
|
10821
|
+
await page.getByText(text, { exact: opts?.exact ?? false }).first().waitFor({ state: "visible", timeout });
|
|
10822
|
+
} catch (err) {
|
|
10823
|
+
throw new ElementNotFoundError(`text:"${text}"`);
|
|
10824
|
+
}
|
|
10825
|
+
}
|
|
10826
|
+
function watchPage(page, opts) {
|
|
10827
|
+
const id = `watch-${Date.now()}`;
|
|
10828
|
+
const changes = [];
|
|
10829
|
+
const intervalMs = opts?.intervalMs ?? 500;
|
|
10830
|
+
const maxChanges = opts?.maxChanges ?? 50;
|
|
10831
|
+
const interval = setInterval(async () => {
|
|
10832
|
+
if (changes.length >= maxChanges)
|
|
10833
|
+
return;
|
|
10834
|
+
try {
|
|
10835
|
+
const change = await page.evaluate((sel) => {
|
|
10836
|
+
const el = sel ? document.querySelector(sel) : document.body;
|
|
10837
|
+
return el ? `${new Date().toISOString()}:${el.textContent?.slice(0, 100)}` : null;
|
|
10838
|
+
}, opts?.selector ?? null);
|
|
10839
|
+
if (change && (changes.length === 0 || changes[changes.length - 1] !== change)) {
|
|
10840
|
+
changes.push(change);
|
|
10841
|
+
}
|
|
10842
|
+
} catch {}
|
|
10843
|
+
}, intervalMs);
|
|
10844
|
+
activeWatches.set(id, { interval, changes });
|
|
10845
|
+
return {
|
|
10846
|
+
id,
|
|
10847
|
+
stop: () => {
|
|
10848
|
+
clearInterval(interval);
|
|
10849
|
+
activeWatches.delete(id);
|
|
10850
|
+
}
|
|
10851
|
+
};
|
|
10852
|
+
}
|
|
10853
|
+
function getWatchChanges(watchId) {
|
|
10854
|
+
return activeWatches.get(watchId)?.changes ?? [];
|
|
10855
|
+
}
|
|
10856
|
+
function stopWatch(watchId) {
|
|
10857
|
+
const w = activeWatches.get(watchId);
|
|
10858
|
+
if (w) {
|
|
10859
|
+
clearInterval(w.interval);
|
|
10860
|
+
activeWatches.delete(watchId);
|
|
10861
|
+
}
|
|
10862
|
+
}
|
|
10863
|
+
function stopAllWatchesForSession(_sessionId) {
|
|
10864
|
+
for (const [id, w] of activeWatches) {
|
|
10865
|
+
clearInterval(w.interval);
|
|
10866
|
+
activeWatches.delete(id);
|
|
10867
|
+
}
|
|
10868
|
+
}
|
|
10869
|
+
async function clickRef(page, sessionId, ref, opts) {
|
|
10870
|
+
try {
|
|
10871
|
+
const locator = getRefLocator(page, sessionId, ref);
|
|
10872
|
+
await locator.click({ timeout: opts?.timeout ?? 1e4 });
|
|
10873
|
+
} catch (err) {
|
|
10874
|
+
if (err instanceof Error && err.message.includes("Ref "))
|
|
10875
|
+
throw new ElementNotFoundError(ref);
|
|
10876
|
+
if (err instanceof Error && err.message.includes("No snapshot"))
|
|
10877
|
+
throw new BrowserError(err.message, "NO_SNAPSHOT");
|
|
10878
|
+
throw new BrowserError(`clickRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "CLICK_REF_FAILED");
|
|
10879
|
+
}
|
|
10880
|
+
}
|
|
10881
|
+
async function typeRef(page, sessionId, ref, text, opts) {
|
|
10882
|
+
try {
|
|
10883
|
+
const locator = getRefLocator(page, sessionId, ref);
|
|
10884
|
+
if (opts?.clear)
|
|
10885
|
+
await locator.fill("", { timeout: opts.timeout ?? 1e4 });
|
|
10886
|
+
await locator.pressSequentially(text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
|
|
10887
|
+
} catch (err) {
|
|
10888
|
+
if (err instanceof Error && err.message.includes("Ref "))
|
|
10889
|
+
throw new ElementNotFoundError(ref);
|
|
10890
|
+
throw new BrowserError(`typeRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "TYPE_REF_FAILED");
|
|
10891
|
+
}
|
|
10892
|
+
}
|
|
10893
|
+
async function fillRef(page, sessionId, ref, value, timeout = 1e4) {
|
|
10894
|
+
try {
|
|
10895
|
+
const locator = getRefLocator(page, sessionId, ref);
|
|
10896
|
+
await locator.fill(value, { timeout });
|
|
10897
|
+
} catch (err) {
|
|
10898
|
+
if (err instanceof Error && err.message.includes("Ref "))
|
|
10899
|
+
throw new ElementNotFoundError(ref);
|
|
10900
|
+
throw new BrowserError(`fillRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "FILL_REF_FAILED");
|
|
10901
|
+
}
|
|
10902
|
+
}
|
|
10903
|
+
async function selectRef(page, sessionId, ref, value, timeout = 1e4) {
|
|
10904
|
+
try {
|
|
10905
|
+
const locator = getRefLocator(page, sessionId, ref);
|
|
10906
|
+
return await locator.selectOption(value, { timeout });
|
|
10907
|
+
} catch (err) {
|
|
10908
|
+
if (err instanceof Error && err.message.includes("Ref "))
|
|
10909
|
+
throw new ElementNotFoundError(ref);
|
|
10910
|
+
throw new BrowserError(`selectRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "SELECT_REF_FAILED");
|
|
10911
|
+
}
|
|
10912
|
+
}
|
|
10913
|
+
async function checkRef(page, sessionId, ref, checked, timeout = 1e4) {
|
|
10914
|
+
try {
|
|
10915
|
+
const locator = getRefLocator(page, sessionId, ref);
|
|
10916
|
+
if (checked)
|
|
10917
|
+
await locator.check({ timeout });
|
|
10918
|
+
else
|
|
10919
|
+
await locator.uncheck({ timeout });
|
|
10920
|
+
} catch (err) {
|
|
10921
|
+
if (err instanceof Error && err.message.includes("Ref "))
|
|
10922
|
+
throw new ElementNotFoundError(ref);
|
|
10923
|
+
throw new BrowserError(`checkRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "CHECK_REF_FAILED");
|
|
10924
|
+
}
|
|
10925
|
+
}
|
|
10926
|
+
async function hoverRef(page, sessionId, ref, timeout = 1e4) {
|
|
10927
|
+
try {
|
|
10928
|
+
const locator = getRefLocator(page, sessionId, ref);
|
|
10929
|
+
await locator.hover({ timeout });
|
|
10930
|
+
} catch (err) {
|
|
10931
|
+
if (err instanceof Error && err.message.includes("Ref "))
|
|
10932
|
+
throw new ElementNotFoundError(ref);
|
|
10933
|
+
throw new BrowserError(`hoverRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "HOVER_REF_FAILED");
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10936
|
+
var RETRYABLE_ERRORS, activeWatches;
|
|
10937
|
+
var init_actions = __esm(() => {
|
|
10938
|
+
init_types();
|
|
10939
|
+
init_snapshot();
|
|
10940
|
+
RETRYABLE_ERRORS = ["Timeout", "timeout", "navigation", "net::ERR", "Target closed"];
|
|
10941
|
+
activeWatches = new Map;
|
|
10942
|
+
});
|
|
10943
|
+
|
|
10944
|
+
// node_modules/sharp/lib/is.js
|
|
10945
|
+
var require_is = __commonJS((exports, module) => {
|
|
10946
|
+
/*!
|
|
10947
|
+
Copyright 2013 Lovell Fuller and others.
|
|
10948
|
+
SPDX-License-Identifier: Apache-2.0
|
|
10949
|
+
*/
|
|
10950
|
+
var defined = (val) => typeof val !== "undefined" && val !== null;
|
|
10951
|
+
var object = (val) => typeof val === "object";
|
|
10952
|
+
var plainObject = (val) => Object.prototype.toString.call(val) === "[object Object]";
|
|
10953
|
+
var fn = (val) => typeof val === "function";
|
|
10954
|
+
var bool = (val) => typeof val === "boolean";
|
|
10955
|
+
var buffer = (val) => val instanceof Buffer;
|
|
10956
|
+
var typedArray = (val) => {
|
|
10957
|
+
if (defined(val)) {
|
|
10958
|
+
switch (val.constructor) {
|
|
10959
|
+
case Uint8Array:
|
|
10960
|
+
case Uint8ClampedArray:
|
|
10961
|
+
case Int8Array:
|
|
10962
|
+
case Uint16Array:
|
|
10963
|
+
case Int16Array:
|
|
10964
|
+
case Uint32Array:
|
|
10965
|
+
case Int32Array:
|
|
10966
|
+
case Float32Array:
|
|
10967
|
+
case Float64Array:
|
|
10968
|
+
return true;
|
|
10969
|
+
}
|
|
10970
|
+
}
|
|
10971
|
+
return false;
|
|
10972
|
+
};
|
|
10973
|
+
var arrayBuffer = (val) => val instanceof ArrayBuffer;
|
|
10974
|
+
var string = (val) => typeof val === "string" && val.length > 0;
|
|
10975
|
+
var number = (val) => typeof val === "number" && !Number.isNaN(val);
|
|
10976
|
+
var integer = (val) => Number.isInteger(val);
|
|
10977
|
+
var inRange = (val, min, max) => val >= min && val <= max;
|
|
10978
|
+
var inArray = (val, list) => list.includes(val);
|
|
10979
|
+
var invalidParameterError = (name, expected, actual) => new Error(`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`);
|
|
10980
|
+
var nativeError = (native, context) => {
|
|
10981
|
+
context.message = native.message;
|
|
10982
|
+
return context;
|
|
10983
|
+
};
|
|
10984
|
+
module.exports = {
|
|
10985
|
+
defined,
|
|
10986
|
+
object,
|
|
10987
|
+
plainObject,
|
|
10988
|
+
fn,
|
|
10989
|
+
bool,
|
|
10990
|
+
buffer,
|
|
10991
|
+
typedArray,
|
|
10992
|
+
arrayBuffer,
|
|
10993
|
+
string,
|
|
10994
|
+
number,
|
|
10995
|
+
integer,
|
|
10996
|
+
inRange,
|
|
10997
|
+
inArray,
|
|
10998
|
+
invalidParameterError,
|
|
10999
|
+
nativeError
|
|
11000
|
+
};
|
|
11001
|
+
});
|
|
11002
|
+
|
|
11003
|
+
// node_modules/detect-libc/lib/process.js
|
|
11004
|
+
var require_process = __commonJS((exports, module) => {
|
|
11005
|
+
var isLinux = () => process.platform === "linux";
|
|
11006
|
+
var report = null;
|
|
11007
|
+
var getReport = () => {
|
|
11008
|
+
if (!report) {
|
|
11009
|
+
if (isLinux() && process.report) {
|
|
11010
|
+
const orig = process.report.excludeNetwork;
|
|
11011
|
+
process.report.excludeNetwork = true;
|
|
11012
|
+
report = process.report.getReport();
|
|
11013
|
+
process.report.excludeNetwork = orig;
|
|
11014
|
+
} else {
|
|
11015
|
+
report = {};
|
|
11016
|
+
}
|
|
11017
|
+
}
|
|
11018
|
+
return report;
|
|
11019
|
+
};
|
|
11020
|
+
module.exports = { isLinux, getReport };
|
|
11021
|
+
});
|
|
11022
|
+
|
|
11023
|
+
// node_modules/detect-libc/lib/filesystem.js
|
|
11024
|
+
var require_filesystem = __commonJS((exports, module) => {
|
|
11025
|
+
var fs = __require("fs");
|
|
11026
|
+
var LDD_PATH = "/usr/bin/ldd";
|
|
11027
|
+
var SELF_PATH = "/proc/self/exe";
|
|
11028
|
+
var MAX_LENGTH = 2048;
|
|
11029
|
+
var readFileSync2 = (path) => {
|
|
11030
|
+
const fd = fs.openSync(path, "r");
|
|
11031
|
+
const buffer = Buffer.alloc(MAX_LENGTH);
|
|
11032
|
+
const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
|
|
11033
|
+
fs.close(fd, () => {});
|
|
11034
|
+
return buffer.subarray(0, bytesRead);
|
|
11035
|
+
};
|
|
11036
|
+
var readFile = (path) => new Promise((resolve, reject) => {
|
|
11037
|
+
fs.open(path, "r", (err, fd) => {
|
|
11038
|
+
if (err) {
|
|
11039
|
+
reject(err);
|
|
11040
|
+
} else {
|
|
11041
|
+
const buffer = Buffer.alloc(MAX_LENGTH);
|
|
11042
|
+
fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
|
|
11043
|
+
resolve(buffer.subarray(0, bytesRead));
|
|
11044
|
+
fs.close(fd, () => {});
|
|
11045
|
+
});
|
|
11046
|
+
}
|
|
11047
|
+
});
|
|
11048
|
+
});
|
|
11049
|
+
module.exports = {
|
|
11050
|
+
LDD_PATH,
|
|
11051
|
+
SELF_PATH,
|
|
11052
|
+
readFileSync: readFileSync2,
|
|
11053
|
+
readFile
|
|
11054
|
+
};
|
|
11055
|
+
});
|
|
11056
|
+
|
|
11057
|
+
// node_modules/detect-libc/lib/elf.js
|
|
11058
|
+
var require_elf = __commonJS((exports, module) => {
|
|
11059
|
+
var interpreterPath = (elf) => {
|
|
11060
|
+
if (elf.length < 64) {
|
|
11061
|
+
return null;
|
|
11062
|
+
}
|
|
11063
|
+
if (elf.readUInt32BE(0) !== 2135247942) {
|
|
11064
|
+
return null;
|
|
11065
|
+
}
|
|
11066
|
+
if (elf.readUInt8(4) !== 2) {
|
|
11067
|
+
return null;
|
|
11068
|
+
}
|
|
11069
|
+
if (elf.readUInt8(5) !== 1) {
|
|
11070
|
+
return null;
|
|
11071
|
+
}
|
|
11072
|
+
const offset = elf.readUInt32LE(32);
|
|
11073
|
+
const size = elf.readUInt16LE(54);
|
|
11074
|
+
const count = elf.readUInt16LE(56);
|
|
11075
|
+
for (let i = 0;i < count; i++) {
|
|
11076
|
+
const headerOffset = offset + i * size;
|
|
11077
|
+
const type2 = elf.readUInt32LE(headerOffset);
|
|
11078
|
+
if (type2 === 3) {
|
|
11079
|
+
const fileOffset = elf.readUInt32LE(headerOffset + 8);
|
|
11080
|
+
const fileSize = elf.readUInt32LE(headerOffset + 32);
|
|
11081
|
+
return elf.subarray(fileOffset, fileOffset + fileSize).toString().replace(/\0.*$/g, "");
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
11084
|
+
return null;
|
|
11085
|
+
};
|
|
11086
|
+
module.exports = {
|
|
11087
|
+
interpreterPath
|
|
10213
11088
|
};
|
|
10214
11089
|
});
|
|
10215
11090
|
|
|
@@ -13725,7 +14600,7 @@ var require_operation = __commonJS((exports, module) => {
|
|
|
13725
14600
|
// node_modules/@img/colour/color.cjs
|
|
13726
14601
|
var require_color = __commonJS((exports, module) => {
|
|
13727
14602
|
var __defProp3 = Object.defineProperty;
|
|
13728
|
-
var
|
|
14603
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
13729
14604
|
var __getOwnPropNames3 = Object.getOwnPropertyNames;
|
|
13730
14605
|
var __hasOwnProp3 = Object.prototype.hasOwnProperty;
|
|
13731
14606
|
var __export3 = (target, all) => {
|
|
@@ -13736,16 +14611,16 @@ var require_color = __commonJS((exports, module) => {
|
|
|
13736
14611
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
13737
14612
|
for (let key of __getOwnPropNames3(from))
|
|
13738
14613
|
if (!__hasOwnProp3.call(to, key) && key !== except)
|
|
13739
|
-
__defProp3(to, key, { get: () => from[key], enumerable: !(desc =
|
|
14614
|
+
__defProp3(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
13740
14615
|
}
|
|
13741
14616
|
return to;
|
|
13742
14617
|
};
|
|
13743
|
-
var
|
|
14618
|
+
var __toCommonJS2 = (mod) => __copyProps(__defProp3({}, "__esModule", { value: true }), mod);
|
|
13744
14619
|
var index_exports = {};
|
|
13745
14620
|
__export3(index_exports, {
|
|
13746
14621
|
default: () => index_default
|
|
13747
14622
|
});
|
|
13748
|
-
module.exports =
|
|
14623
|
+
module.exports = __toCommonJS2(index_exports);
|
|
13749
14624
|
var colors = {
|
|
13750
14625
|
aliceblue: [240, 248, 255],
|
|
13751
14626
|
antiquewhite: [250, 235, 215],
|
|
@@ -17389,6 +18264,7 @@ function enableNetworkLogging(page, sessionId) {
|
|
|
17389
18264
|
requestStart.clear();
|
|
17390
18265
|
};
|
|
17391
18266
|
}
|
|
18267
|
+
var HAR_MAX_ENTRIES = 5000;
|
|
17392
18268
|
function startHAR(page) {
|
|
17393
18269
|
const entries = [];
|
|
17394
18270
|
const requestStart = new Map;
|
|
@@ -17427,7 +18303,8 @@ function startHAR(page) {
|
|
|
17427
18303
|
},
|
|
17428
18304
|
timings: { send: 0, wait: duration, receive: 0 }
|
|
17429
18305
|
};
|
|
17430
|
-
entries.
|
|
18306
|
+
if (entries.length < HAR_MAX_ENTRIES)
|
|
18307
|
+
entries.push(entry);
|
|
17431
18308
|
};
|
|
17432
18309
|
const onRequestFailed = (req) => {
|
|
17433
18310
|
requestStart.delete(req.url() + req.method());
|
|
@@ -17545,49 +18422,8 @@ async function applyStealthPatches(page) {
|
|
|
17545
18422
|
});
|
|
17546
18423
|
}
|
|
17547
18424
|
|
|
17548
|
-
// src/lib/dialogs.ts
|
|
17549
|
-
var pendingDialogs = new Map;
|
|
17550
|
-
var AUTO_DISMISS_MS = 5000;
|
|
17551
|
-
function setupDialogHandler(page, sessionId) {
|
|
17552
|
-
const onDialog = (dialog) => {
|
|
17553
|
-
const info = {
|
|
17554
|
-
type: dialog.type(),
|
|
17555
|
-
message: dialog.message(),
|
|
17556
|
-
default_value: dialog.defaultValue(),
|
|
17557
|
-
timestamp: new Date().toISOString()
|
|
17558
|
-
};
|
|
17559
|
-
const autoTimer = setTimeout(() => {
|
|
17560
|
-
try {
|
|
17561
|
-
dialog.dismiss().catch(() => {});
|
|
17562
|
-
} catch {}
|
|
17563
|
-
const list = pendingDialogs.get(sessionId);
|
|
17564
|
-
if (list) {
|
|
17565
|
-
const idx = list.findIndex((p) => p.dialog === dialog);
|
|
17566
|
-
if (idx >= 0)
|
|
17567
|
-
list.splice(idx, 1);
|
|
17568
|
-
if (list.length === 0)
|
|
17569
|
-
pendingDialogs.delete(sessionId);
|
|
17570
|
-
}
|
|
17571
|
-
}, AUTO_DISMISS_MS);
|
|
17572
|
-
const pending = { dialog, info, autoTimer };
|
|
17573
|
-
if (!pendingDialogs.has(sessionId)) {
|
|
17574
|
-
pendingDialogs.set(sessionId, []);
|
|
17575
|
-
}
|
|
17576
|
-
pendingDialogs.get(sessionId).push(pending);
|
|
17577
|
-
};
|
|
17578
|
-
page.on("dialog", onDialog);
|
|
17579
|
-
return () => {
|
|
17580
|
-
page.off("dialog", onDialog);
|
|
17581
|
-
const list = pendingDialogs.get(sessionId);
|
|
17582
|
-
if (list) {
|
|
17583
|
-
for (const p of list)
|
|
17584
|
-
clearTimeout(p.autoTimer);
|
|
17585
|
-
pendingDialogs.delete(sessionId);
|
|
17586
|
-
}
|
|
17587
|
-
};
|
|
17588
|
-
}
|
|
17589
|
-
|
|
17590
18425
|
// src/lib/session.ts
|
|
18426
|
+
init_dialogs();
|
|
17591
18427
|
var handles = new Map;
|
|
17592
18428
|
var pool = new BrowserPool(5);
|
|
17593
18429
|
var SESSION_TTL_MS = parseInt(process.env["SESSION_TTL_MINUTES"] ?? "10", 10) * 60000;
|
|
@@ -17603,6 +18439,20 @@ var ttlInterval = setInterval(async () => {
|
|
|
17603
18439
|
}, 60000);
|
|
17604
18440
|
if (ttlInterval.unref)
|
|
17605
18441
|
ttlInterval.unref();
|
|
18442
|
+
var DB_PRUNE_INTERVAL_MS = 30 * 60000;
|
|
18443
|
+
var DB_RETENTION_HOURS = 24;
|
|
18444
|
+
var dbPruneInterval = setInterval(() => {
|
|
18445
|
+
try {
|
|
18446
|
+
const { getDatabase: getDatabase2 } = (init_schema(), __toCommonJS(exports_schema));
|
|
18447
|
+
const db = getDatabase2();
|
|
18448
|
+
const cutoff = new Date(Date.now() - DB_RETENTION_HOURS * 3600000).toISOString();
|
|
18449
|
+
db.prepare("DELETE FROM network_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
|
|
18450
|
+
db.prepare("DELETE FROM console_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
|
|
18451
|
+
db.prepare("DELETE FROM snapshots WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
|
|
18452
|
+
} catch {}
|
|
18453
|
+
}, DB_PRUNE_INTERVAL_MS);
|
|
18454
|
+
if (dbPruneInterval.unref)
|
|
18455
|
+
dbPruneInterval.unref();
|
|
17606
18456
|
function createBunProxy(view) {
|
|
17607
18457
|
return view;
|
|
17608
18458
|
}
|
|
@@ -17781,142 +18631,27 @@ async function closeSession2(sessionId) {
|
|
|
17781
18631
|
}
|
|
17782
18632
|
handles.delete(sessionId);
|
|
17783
18633
|
}
|
|
17784
|
-
return closeSession(sessionId);
|
|
17785
|
-
}
|
|
17786
|
-
function listSessions2(filter) {
|
|
17787
|
-
return listSessions(filter);
|
|
17788
|
-
}
|
|
17789
|
-
|
|
17790
|
-
// src/lib/actions.ts
|
|
17791
|
-
init_types();
|
|
17792
|
-
|
|
17793
|
-
// src/lib/snapshot.ts
|
|
17794
|
-
var lastSnapshots = new Map;
|
|
17795
|
-
var sessionRefMaps = new Map;
|
|
17796
|
-
|
|
17797
|
-
// src/lib/self-heal.ts
|
|
17798
|
-
async function healSelector(page, selector, sessionId) {
|
|
17799
|
-
const attempts = [];
|
|
17800
|
-
attempts.push(`selector: ${selector}`);
|
|
17801
18634
|
try {
|
|
17802
|
-
const
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
}
|
|
18635
|
+
const { clearLastSnapshot: clearLastSnapshot2, clearSessionRefs: clearSessionRefs2 } = await Promise.resolve().then(() => (init_snapshot(), exports_snapshot));
|
|
18636
|
+
clearLastSnapshot2(sessionId);
|
|
18637
|
+
clearSessionRefs2(sessionId);
|
|
17806
18638
|
} catch {}
|
|
17807
|
-
if (!selector.startsWith("#") && !selector.startsWith(".") && !selector.startsWith("[") && !selector.includes(">") && !selector.includes(" ")) {
|
|
17808
|
-
attempts.push(`text: "${selector}"`);
|
|
17809
|
-
try {
|
|
17810
|
-
const loc = page.getByText(selector, { exact: false }).first();
|
|
17811
|
-
if (await loc.count() > 0) {
|
|
17812
|
-
return { found: true, locator: loc, method: "text", healed: true, attempts };
|
|
17813
|
-
}
|
|
17814
|
-
} catch {}
|
|
17815
|
-
}
|
|
17816
|
-
const roleMap = {
|
|
17817
|
-
button: ["button", "submit", "reset"],
|
|
17818
|
-
link: ["a"],
|
|
17819
|
-
input: ["input", "textarea"],
|
|
17820
|
-
heading: ["h1", "h2", "h3", "h4", "h5", "h6"]
|
|
17821
|
-
};
|
|
17822
|
-
const nameHint = selector.replace(/^[#.]/, "").replace(/[-_]/g, " ").toLowerCase();
|
|
17823
|
-
for (const [role, tags] of Object.entries(roleMap)) {
|
|
17824
|
-
attempts.push(`role: ${role} name~="${nameHint}"`);
|
|
17825
|
-
try {
|
|
17826
|
-
const loc = page.getByRole(role, { name: new RegExp(nameHint.split(" ")[0], "i") }).first();
|
|
17827
|
-
if (await loc.count() > 0) {
|
|
17828
|
-
return { found: true, locator: loc, method: "role", healed: true, attempts };
|
|
17829
|
-
}
|
|
17830
|
-
} catch {}
|
|
17831
|
-
}
|
|
17832
|
-
if (selector.startsWith("#")) {
|
|
17833
|
-
const idPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
|
|
17834
|
-
const partialSel = `[id*="${idPart}"]`;
|
|
17835
|
-
attempts.push(`partial_id: ${partialSel}`);
|
|
17836
|
-
try {
|
|
17837
|
-
const loc = page.locator(partialSel).first();
|
|
17838
|
-
if (await loc.count() > 0) {
|
|
17839
|
-
return { found: true, locator: loc, method: "partial_id", healed: true, attempts };
|
|
17840
|
-
}
|
|
17841
|
-
} catch {}
|
|
17842
|
-
}
|
|
17843
|
-
if (selector.startsWith(".")) {
|
|
17844
|
-
const classPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
|
|
17845
|
-
const partialSel = `[class*="${classPart}"]`;
|
|
17846
|
-
attempts.push(`partial_class: ${partialSel}`);
|
|
17847
|
-
try {
|
|
17848
|
-
const loc = page.locator(partialSel).first();
|
|
17849
|
-
if (await loc.count() > 0) {
|
|
17850
|
-
return { found: true, locator: loc, method: "partial_class", healed: true, attempts };
|
|
17851
|
-
}
|
|
17852
|
-
} catch {}
|
|
17853
|
-
}
|
|
17854
|
-
return { found: false, locator: null, method: "none", healed: false, attempts };
|
|
17855
|
-
}
|
|
17856
|
-
|
|
17857
|
-
// src/lib/actions.ts
|
|
17858
|
-
async function click(page, selector, opts) {
|
|
17859
18639
|
try {
|
|
17860
|
-
await
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
delay: opts?.delay,
|
|
17864
|
-
timeout: opts?.timeout ?? 1e4
|
|
17865
|
-
});
|
|
17866
|
-
return {};
|
|
17867
|
-
} catch (originalError) {
|
|
17868
|
-
if (opts?.selfHeal !== false) {
|
|
17869
|
-
const result = await healSelector(page, selector);
|
|
17870
|
-
if (result.found && result.locator) {
|
|
17871
|
-
await result.locator.click({
|
|
17872
|
-
button: opts?.button ?? "left",
|
|
17873
|
-
timeout: opts?.timeout ?? 1e4
|
|
17874
|
-
});
|
|
17875
|
-
return { healed: true, method: result.method, attempts: result.attempts };
|
|
17876
|
-
}
|
|
17877
|
-
}
|
|
17878
|
-
if (originalError instanceof Error && originalError.message.includes("not found")) {
|
|
17879
|
-
throw new ElementNotFoundError(selector);
|
|
17880
|
-
}
|
|
17881
|
-
throw new BrowserError(`Click failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "CLICK_FAILED");
|
|
17882
|
-
}
|
|
17883
|
-
}
|
|
17884
|
-
async function type(page, selector, text, opts) {
|
|
18640
|
+
const { stopAllWatchesForSession: stopAllWatchesForSession2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
|
|
18641
|
+
stopAllWatchesForSession2(sessionId);
|
|
18642
|
+
} catch {}
|
|
17885
18643
|
try {
|
|
17886
|
-
|
|
17887
|
-
|
|
17888
|
-
|
|
17889
|
-
|
|
17890
|
-
return {};
|
|
17891
|
-
} catch (originalError) {
|
|
17892
|
-
if (opts?.selfHeal !== false) {
|
|
17893
|
-
const result = await healSelector(page, selector);
|
|
17894
|
-
if (result.found && result.locator) {
|
|
17895
|
-
if (opts?.clear)
|
|
17896
|
-
await result.locator.fill("", { timeout: opts?.timeout ?? 1e4 });
|
|
17897
|
-
await result.locator.pressSequentially(text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
|
|
17898
|
-
return { healed: true, method: result.method, attempts: result.attempts };
|
|
17899
|
-
}
|
|
17900
|
-
}
|
|
17901
|
-
if (originalError instanceof Error && originalError.message.includes("not found")) {
|
|
17902
|
-
throw new ElementNotFoundError(selector);
|
|
17903
|
-
}
|
|
17904
|
-
throw new BrowserError(`Type failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "TYPE_FAILED");
|
|
17905
|
-
}
|
|
17906
|
-
}
|
|
17907
|
-
async function scroll(page, direction = "down", amount = 300) {
|
|
17908
|
-
const x = direction === "left" ? -amount : direction === "right" ? amount : 0;
|
|
17909
|
-
const y = direction === "up" ? -amount : direction === "down" ? amount : 0;
|
|
17910
|
-
await page.evaluate(({ x: x2, y: y2 }) => window.scrollBy(x2, y2), { x, y });
|
|
18644
|
+
const { clearDialogs: clearDialogs2 } = await Promise.resolve().then(() => (init_dialogs(), exports_dialogs));
|
|
18645
|
+
clearDialogs2(sessionId);
|
|
18646
|
+
} catch {}
|
|
18647
|
+
return closeSession(sessionId);
|
|
17911
18648
|
}
|
|
17912
|
-
|
|
17913
|
-
|
|
17914
|
-
await page.goto(url, { waitUntil: "domcontentloaded", timeout });
|
|
17915
|
-
} catch (err) {
|
|
17916
|
-
throw new NavigationError(url, err instanceof Error ? err.message : String(err));
|
|
17917
|
-
}
|
|
18649
|
+
function listSessions2(filter) {
|
|
18650
|
+
return listSessions(filter);
|
|
17918
18651
|
}
|
|
17919
|
-
|
|
18652
|
+
|
|
18653
|
+
// src/server/index.ts
|
|
18654
|
+
init_actions();
|
|
17920
18655
|
|
|
17921
18656
|
// src/lib/extractor.ts
|
|
17922
18657
|
async function getText(page, selector) {
|
|
@@ -18398,6 +19133,7 @@ async function crawl(startUrl, opts = {}) {
|
|
|
18398
19133
|
|
|
18399
19134
|
// src/lib/recorder.ts
|
|
18400
19135
|
init_recordings();
|
|
19136
|
+
init_actions();
|
|
18401
19137
|
init_types();
|
|
18402
19138
|
var activeRecordings = new Map;
|
|
18403
19139
|
async function replayRecording(recordingId, page) {
|