lobster-cli 0.1.0 → 0.2.0
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/LICENSE +21 -0
- package/README.md +147 -269
- package/dist/browser/chrome-attach.js +102 -0
- package/dist/browser/chrome-attach.js.map +1 -0
- package/dist/browser/dom/compact-snapshot.js +162 -0
- package/dist/browser/dom/compact-snapshot.js.map +1 -0
- package/dist/browser/dom/index.js +160 -0
- package/dist/browser/dom/index.js.map +1 -1
- package/dist/browser/index.js +907 -70
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/manager.js +443 -11
- package/dist/browser/manager.js.map +1 -1
- package/dist/browser/page-adapter.js +370 -1
- package/dist/browser/page-adapter.js.map +1 -1
- package/dist/browser/profiles.js +238 -0
- package/dist/browser/profiles.js.map +1 -0
- package/dist/browser/semantic-find.js +152 -0
- package/dist/browser/semantic-find.js.map +1 -0
- package/dist/browser/stealth.js +187 -0
- package/dist/browser/stealth.js.map +1 -0
- package/dist/config/index.js +8 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.js +8 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/domain-guard.js +103 -0
- package/dist/domain-guard.js.map +1 -0
- package/dist/index.js +851 -48
- package/dist/index.js.map +1 -1
- package/dist/lib.js +1141 -244
- package/dist/lib.js.map +1 -1
- package/dist/router/index.js +862 -61
- package/dist/router/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -455,6 +455,161 @@ var SNAPSHOT_SCRIPT = `
|
|
|
455
455
|
})()
|
|
456
456
|
`;
|
|
457
457
|
|
|
458
|
+
// src/browser/dom/compact-snapshot.ts
|
|
459
|
+
var COMPACT_SNAPSHOT_SCRIPT = `
|
|
460
|
+
(() => {
|
|
461
|
+
const TOKEN_BUDGET = 800;
|
|
462
|
+
const CHARS_PER_TOKEN = 4;
|
|
463
|
+
|
|
464
|
+
const INTERACTIVE_TAGS = new Set([
|
|
465
|
+
'a','button','input','select','textarea','details','summary','label',
|
|
466
|
+
]);
|
|
467
|
+
const INTERACTIVE_ROLES = new Set([
|
|
468
|
+
'button','link','textbox','checkbox','radio','combobox','listbox',
|
|
469
|
+
'menu','menuitem','tab','switch','slider','searchbox','spinbutton',
|
|
470
|
+
'option','menuitemcheckbox','menuitemradio','treeitem',
|
|
471
|
+
]);
|
|
472
|
+
const LANDMARK_TAGS = new Map([
|
|
473
|
+
['nav', 'Navigation'],
|
|
474
|
+
['main', 'Main Content'],
|
|
475
|
+
['header', 'Header'],
|
|
476
|
+
['footer', 'Footer'],
|
|
477
|
+
['aside', 'Sidebar'],
|
|
478
|
+
['form', 'Form'],
|
|
479
|
+
]);
|
|
480
|
+
const LANDMARK_ROLES = new Map([
|
|
481
|
+
['navigation', 'Navigation'],
|
|
482
|
+
['main', 'Main Content'],
|
|
483
|
+
['banner', 'Header'],
|
|
484
|
+
['contentinfo', 'Footer'],
|
|
485
|
+
['complementary', 'Sidebar'],
|
|
486
|
+
['search', 'Search'],
|
|
487
|
+
['dialog', 'Dialog'],
|
|
488
|
+
]);
|
|
489
|
+
|
|
490
|
+
function isVisible(el) {
|
|
491
|
+
if (el.offsetWidth === 0 && el.offsetHeight === 0 && el.tagName !== 'INPUT') return false;
|
|
492
|
+
const s = getComputedStyle(el);
|
|
493
|
+
return s.display !== 'none' && s.visibility !== 'hidden' && s.opacity !== '0';
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function isInteractive(el) {
|
|
497
|
+
const tag = el.tagName.toLowerCase();
|
|
498
|
+
if (INTERACTIVE_TAGS.has(tag)) {
|
|
499
|
+
if (el.disabled) return false;
|
|
500
|
+
if (tag === 'input' && el.type === 'hidden') return false;
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
const role = el.getAttribute('role');
|
|
504
|
+
if (role && INTERACTIVE_ROLES.has(role)) return true;
|
|
505
|
+
if (el.contentEditable === 'true') return true;
|
|
506
|
+
if (el.tabIndex >= 0 && el.getAttribute('tabindex') !== null) return true;
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function getRole(el) {
|
|
511
|
+
const role = el.getAttribute('role');
|
|
512
|
+
if (role) return role;
|
|
513
|
+
const tag = el.tagName.toLowerCase();
|
|
514
|
+
if (tag === 'a') return 'link';
|
|
515
|
+
if (tag === 'button' || tag === 'summary') return 'button';
|
|
516
|
+
if (tag === 'input') return el.type || 'text';
|
|
517
|
+
if (tag === 'select') return 'select';
|
|
518
|
+
if (tag === 'textarea') return 'textarea';
|
|
519
|
+
if (tag === 'label') return 'label';
|
|
520
|
+
return tag;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function getName(el) {
|
|
524
|
+
return (
|
|
525
|
+
el.getAttribute('aria-label') ||
|
|
526
|
+
el.getAttribute('alt') ||
|
|
527
|
+
el.getAttribute('title') ||
|
|
528
|
+
el.getAttribute('placeholder') ||
|
|
529
|
+
(el.tagName === 'INPUT' && (el.type === 'submit' || el.type === 'button') ? el.value : '') ||
|
|
530
|
+
(el.id ? document.querySelector('label[for="' + el.id + '"]')?.textContent?.trim() : '') ||
|
|
531
|
+
(el.children.length <= 2 ? el.textContent?.trim() : '') ||
|
|
532
|
+
''
|
|
533
|
+
).slice(0, 60);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function getValue(el) {
|
|
537
|
+
const tag = el.tagName.toLowerCase();
|
|
538
|
+
if (tag === 'input') {
|
|
539
|
+
const type = el.type || 'text';
|
|
540
|
+
if (type === 'checkbox' || type === 'radio') return el.checked ? 'checked' : 'unchecked';
|
|
541
|
+
if (type === 'password') return el.value ? '****' : '';
|
|
542
|
+
return el.value ? el.value.slice(0, 30) : '';
|
|
543
|
+
}
|
|
544
|
+
if (tag === 'textarea') return el.value ? el.value.slice(0, 30) : '';
|
|
545
|
+
if (tag === 'select' && el.selectedOptions?.length) return el.selectedOptions[0].text.slice(0, 30);
|
|
546
|
+
return '';
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Collect elements
|
|
550
|
+
let idx = 0;
|
|
551
|
+
let charsUsed = 0;
|
|
552
|
+
const lines = [];
|
|
553
|
+
let lastLandmark = '';
|
|
554
|
+
|
|
555
|
+
// Page header
|
|
556
|
+
const scrollY = window.scrollY;
|
|
557
|
+
const scrollMax = document.documentElement.scrollHeight - window.innerHeight;
|
|
558
|
+
const scrollPct = scrollMax > 0 ? Math.round((scrollY / scrollMax) * 100) : 0;
|
|
559
|
+
const header = 'url: ' + location.href + ' | scroll: ' + scrollPct + '%';
|
|
560
|
+
lines.push(header);
|
|
561
|
+
charsUsed += header.length;
|
|
562
|
+
|
|
563
|
+
// Walk DOM
|
|
564
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);
|
|
565
|
+
let node;
|
|
566
|
+
while ((node = walker.nextNode())) {
|
|
567
|
+
if (!isVisible(node)) continue;
|
|
568
|
+
|
|
569
|
+
const tag = node.tagName.toLowerCase();
|
|
570
|
+
if (['script','style','noscript','svg','path','meta','link','head','template'].includes(tag)) continue;
|
|
571
|
+
|
|
572
|
+
// Check for landmark
|
|
573
|
+
const role = node.getAttribute('role');
|
|
574
|
+
const landmark = LANDMARK_TAGS.get(tag) || (role ? LANDMARK_ROLES.get(role) : null);
|
|
575
|
+
if (landmark && landmark !== lastLandmark) {
|
|
576
|
+
const sectionLine = '--- ' + landmark + ' ---';
|
|
577
|
+
if (charsUsed + sectionLine.length > TOKEN_BUDGET * CHARS_PER_TOKEN) break;
|
|
578
|
+
lines.push(sectionLine);
|
|
579
|
+
charsUsed += sectionLine.length;
|
|
580
|
+
lastLandmark = landmark;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Only emit interactive elements
|
|
584
|
+
if (!isInteractive(node)) continue;
|
|
585
|
+
|
|
586
|
+
const elRole = getRole(node);
|
|
587
|
+
const name = getName(node);
|
|
588
|
+
const value = getValue(node);
|
|
589
|
+
|
|
590
|
+
// Build compact line
|
|
591
|
+
let line = '[' + idx + '] ' + elRole;
|
|
592
|
+
if (name) line += ' "' + name.replace(/"/g, "'") + '"';
|
|
593
|
+
if (value) line += ' val="' + value.replace(/"/g, "'") + '"';
|
|
594
|
+
|
|
595
|
+
// Check token budget
|
|
596
|
+
if (charsUsed + line.length > TOKEN_BUDGET * CHARS_PER_TOKEN) {
|
|
597
|
+
lines.push('... (' + (document.querySelectorAll('a,button,input,select,textarea,[role]').length - idx) + ' more elements)');
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Annotate element with ref for clicking
|
|
602
|
+
try { node.dataset.ref = String(idx); } catch {}
|
|
603
|
+
|
|
604
|
+
lines.push(line);
|
|
605
|
+
charsUsed += line.length;
|
|
606
|
+
idx++;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return lines.join('\\n');
|
|
610
|
+
})()
|
|
611
|
+
`;
|
|
612
|
+
|
|
458
613
|
// src/browser/dom/semantic-tree.ts
|
|
459
614
|
var SEMANTIC_TREE_SCRIPT = `
|
|
460
615
|
(() => {
|
|
@@ -980,6 +1135,64 @@ var FORM_STATE_SCRIPT = `
|
|
|
980
1135
|
})()
|
|
981
1136
|
`;
|
|
982
1137
|
|
|
1138
|
+
// src/browser/dom/interactive.ts
|
|
1139
|
+
var INTERACTIVE_ELEMENTS_SCRIPT = `
|
|
1140
|
+
(() => {
|
|
1141
|
+
const results = [];
|
|
1142
|
+
|
|
1143
|
+
function classify(el) {
|
|
1144
|
+
const tag = el.tagName.toLowerCase();
|
|
1145
|
+
const role = el.getAttribute('role');
|
|
1146
|
+
const types = [];
|
|
1147
|
+
|
|
1148
|
+
// Native interactive
|
|
1149
|
+
if (['a', 'button', 'input', 'select', 'textarea', 'details', 'summary'].includes(tag)) {
|
|
1150
|
+
types.push('native');
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// ARIA role interactive
|
|
1154
|
+
if (role && ['button', 'link', 'textbox', 'checkbox', 'radio', 'combobox', 'tab', 'switch', 'menuitem', 'slider'].includes(role)) {
|
|
1155
|
+
types.push('aria');
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// Contenteditable
|
|
1159
|
+
if (el.contentEditable === 'true') types.push('contenteditable');
|
|
1160
|
+
|
|
1161
|
+
// Focusable
|
|
1162
|
+
if (el.tabIndex >= 0 && el.getAttribute('tabindex') !== null) types.push('focusable');
|
|
1163
|
+
|
|
1164
|
+
// Has click listener (approximate)
|
|
1165
|
+
if (el.onclick) types.push('listener');
|
|
1166
|
+
|
|
1167
|
+
return types;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
let idx = 0;
|
|
1171
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);
|
|
1172
|
+
let node;
|
|
1173
|
+
while (node = walker.nextNode()) {
|
|
1174
|
+
const types = classify(node);
|
|
1175
|
+
if (types.length === 0) continue;
|
|
1176
|
+
|
|
1177
|
+
const style = getComputedStyle(node);
|
|
1178
|
+
if (style.display === 'none' || style.visibility === 'hidden') continue;
|
|
1179
|
+
|
|
1180
|
+
const rect = node.getBoundingClientRect();
|
|
1181
|
+
results.push({
|
|
1182
|
+
index: idx++,
|
|
1183
|
+
tag: node.tagName.toLowerCase(),
|
|
1184
|
+
role: node.getAttribute('role') || '',
|
|
1185
|
+
text: (node.textContent || '').trim().slice(0, 100),
|
|
1186
|
+
types,
|
|
1187
|
+
ariaLabel: node.getAttribute('aria-label') || '',
|
|
1188
|
+
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
return results;
|
|
1193
|
+
})()
|
|
1194
|
+
`;
|
|
1195
|
+
|
|
983
1196
|
// src/browser/interceptor.ts
|
|
984
1197
|
function buildInterceptorScript(pattern) {
|
|
985
1198
|
return `
|
|
@@ -1036,6 +1249,155 @@ var GET_INTERCEPTED_SCRIPT = `
|
|
|
1036
1249
|
})()
|
|
1037
1250
|
`;
|
|
1038
1251
|
|
|
1252
|
+
// src/browser/semantic-find.ts
|
|
1253
|
+
var SYNONYMS = {
|
|
1254
|
+
btn: ["button"],
|
|
1255
|
+
button: ["btn", "submit", "click"],
|
|
1256
|
+
submit: ["go", "send", "ok", "confirm", "done", "button"],
|
|
1257
|
+
search: ["find", "lookup", "query", "filter"],
|
|
1258
|
+
login: ["signin", "sign-in", "log-in", "authenticate"],
|
|
1259
|
+
signup: ["register", "create-account", "sign-up", "join"],
|
|
1260
|
+
logout: ["signout", "sign-out", "log-out"],
|
|
1261
|
+
close: ["dismiss", "x", "cancel", "exit"],
|
|
1262
|
+
menu: ["nav", "navigation", "hamburger", "sidebar"],
|
|
1263
|
+
nav: ["navigation", "menu", "navbar"],
|
|
1264
|
+
input: ["field", "textbox", "text", "entry"],
|
|
1265
|
+
email: ["mail", "e-mail"],
|
|
1266
|
+
password: ["pass", "pwd", "secret"],
|
|
1267
|
+
next: ["continue", "forward", "proceed"],
|
|
1268
|
+
back: ["previous", "return", "go-back"],
|
|
1269
|
+
save: ["store", "keep", "persist"],
|
|
1270
|
+
delete: ["remove", "trash", "discard", "destroy"],
|
|
1271
|
+
edit: ["modify", "change", "update"],
|
|
1272
|
+
add: ["create", "new", "plus", "insert"],
|
|
1273
|
+
settings: ["preferences", "config", "options", "gear"],
|
|
1274
|
+
profile: ["account", "user", "avatar"],
|
|
1275
|
+
home: ["main", "dashboard", "start"],
|
|
1276
|
+
link: ["anchor", "href", "url"],
|
|
1277
|
+
select: ["dropdown", "combo", "picker", "choose"],
|
|
1278
|
+
checkbox: ["check", "toggle", "tick"],
|
|
1279
|
+
upload: ["attach", "file", "browse"],
|
|
1280
|
+
download: ["save", "export"]
|
|
1281
|
+
};
|
|
1282
|
+
var ROLE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1283
|
+
"button",
|
|
1284
|
+
"link",
|
|
1285
|
+
"input",
|
|
1286
|
+
"textbox",
|
|
1287
|
+
"checkbox",
|
|
1288
|
+
"radio",
|
|
1289
|
+
"select",
|
|
1290
|
+
"dropdown",
|
|
1291
|
+
"tab",
|
|
1292
|
+
"menu",
|
|
1293
|
+
"menuitem",
|
|
1294
|
+
"switch",
|
|
1295
|
+
"slider",
|
|
1296
|
+
"combobox",
|
|
1297
|
+
"searchbox",
|
|
1298
|
+
"option"
|
|
1299
|
+
]);
|
|
1300
|
+
function tokenize(text) {
|
|
1301
|
+
return text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/[\s-]+/).filter((t) => t.length > 0);
|
|
1302
|
+
}
|
|
1303
|
+
function expandSynonyms(tokens) {
|
|
1304
|
+
const expanded = new Set(tokens);
|
|
1305
|
+
for (const token of tokens) {
|
|
1306
|
+
const syns = SYNONYMS[token];
|
|
1307
|
+
if (syns) {
|
|
1308
|
+
for (const syn of syns) expanded.add(syn);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return expanded;
|
|
1312
|
+
}
|
|
1313
|
+
function freqMap(tokens) {
|
|
1314
|
+
const map = /* @__PURE__ */ new Map();
|
|
1315
|
+
for (const t of tokens) {
|
|
1316
|
+
map.set(t, (map.get(t) || 0) + 1);
|
|
1317
|
+
}
|
|
1318
|
+
return map;
|
|
1319
|
+
}
|
|
1320
|
+
function jaccardScore(queryTokens, descTokens) {
|
|
1321
|
+
const qFreq = freqMap(queryTokens);
|
|
1322
|
+
const dFreq = freqMap(descTokens);
|
|
1323
|
+
let intersection = 0;
|
|
1324
|
+
let union = 0;
|
|
1325
|
+
const allTokens = /* @__PURE__ */ new Set([...qFreq.keys(), ...dFreq.keys()]);
|
|
1326
|
+
for (const token of allTokens) {
|
|
1327
|
+
const qCount = qFreq.get(token) || 0;
|
|
1328
|
+
const dCount = dFreq.get(token) || 0;
|
|
1329
|
+
intersection += Math.min(qCount, dCount);
|
|
1330
|
+
union += Math.max(qCount, dCount);
|
|
1331
|
+
}
|
|
1332
|
+
return union === 0 ? 0 : intersection / union;
|
|
1333
|
+
}
|
|
1334
|
+
function prefixScore(queryTokens, descTokens) {
|
|
1335
|
+
if (queryTokens.length === 0 || descTokens.length === 0) return 0;
|
|
1336
|
+
let matches = 0;
|
|
1337
|
+
for (const qt of queryTokens) {
|
|
1338
|
+
if (qt.length < 3) continue;
|
|
1339
|
+
for (const dt of descTokens) {
|
|
1340
|
+
if (dt.startsWith(qt) || qt.startsWith(dt)) {
|
|
1341
|
+
matches += 0.5;
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
return Math.min(matches / queryTokens.length, 0.3);
|
|
1347
|
+
}
|
|
1348
|
+
function roleBoost(queryTokens, elementRole) {
|
|
1349
|
+
const roleLower = elementRole.toLowerCase();
|
|
1350
|
+
for (const qt of queryTokens) {
|
|
1351
|
+
if (ROLE_KEYWORDS.has(qt) && roleLower.includes(qt)) {
|
|
1352
|
+
return 0.2;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return 0;
|
|
1356
|
+
}
|
|
1357
|
+
function scoreElement(queryTokens, queryExpanded, element) {
|
|
1358
|
+
const descParts = [
|
|
1359
|
+
element.text,
|
|
1360
|
+
element.role,
|
|
1361
|
+
element.tag,
|
|
1362
|
+
element.ariaLabel
|
|
1363
|
+
].filter(Boolean);
|
|
1364
|
+
const descText = descParts.join(" ");
|
|
1365
|
+
const descTokens = tokenize(descText);
|
|
1366
|
+
if (descTokens.length === 0) return 0;
|
|
1367
|
+
const descExpanded = expandSynonyms(descTokens);
|
|
1368
|
+
const expandedQueryTokens = [...queryExpanded];
|
|
1369
|
+
const expandedDescTokens = [...descExpanded];
|
|
1370
|
+
const jaccard = jaccardScore(expandedQueryTokens, expandedDescTokens);
|
|
1371
|
+
const prefix = prefixScore(queryTokens, descTokens);
|
|
1372
|
+
const role = roleBoost(queryTokens, element.role || element.tag);
|
|
1373
|
+
const queryStr = queryTokens.join(" ");
|
|
1374
|
+
const descStr = descTokens.join(" ");
|
|
1375
|
+
const exactBonus = descStr.includes(queryStr) ? 0.3 : 0;
|
|
1376
|
+
return Math.min(jaccard + prefix + role + exactBonus, 1);
|
|
1377
|
+
}
|
|
1378
|
+
function semanticFind(elements, query, options) {
|
|
1379
|
+
const maxResults = options?.maxResults ?? 5;
|
|
1380
|
+
const minScore = options?.minScore ?? 0.3;
|
|
1381
|
+
const queryTokens = tokenize(query);
|
|
1382
|
+
if (queryTokens.length === 0) return [];
|
|
1383
|
+
const queryExpanded = expandSynonyms(queryTokens);
|
|
1384
|
+
const scored = [];
|
|
1385
|
+
for (const el of elements) {
|
|
1386
|
+
const score = scoreElement(queryTokens, queryExpanded, el);
|
|
1387
|
+
if (score >= minScore) {
|
|
1388
|
+
scored.push({
|
|
1389
|
+
ref: el.index,
|
|
1390
|
+
score: Math.round(score * 100) / 100,
|
|
1391
|
+
text: (el.text || el.ariaLabel || "").slice(0, 60),
|
|
1392
|
+
role: el.role || el.tag,
|
|
1393
|
+
tag: el.tag
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
scored.sort((a, b) => b.score - a.score);
|
|
1398
|
+
return scored.slice(0, maxResults);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1039
1401
|
// src/browser/page-adapter.ts
|
|
1040
1402
|
var PuppeteerPage = class {
|
|
1041
1403
|
page;
|
|
@@ -1063,7 +1425,10 @@ var PuppeteerPage = class {
|
|
|
1063
1425
|
async evaluate(js) {
|
|
1064
1426
|
return this.page.evaluate(js);
|
|
1065
1427
|
}
|
|
1066
|
-
async snapshot(
|
|
1428
|
+
async snapshot(opts) {
|
|
1429
|
+
if (opts?.compact) {
|
|
1430
|
+
return this.page.evaluate(COMPACT_SNAPSHOT_SCRIPT);
|
|
1431
|
+
}
|
|
1067
1432
|
return this.page.evaluate(SNAPSHOT_SCRIPT);
|
|
1068
1433
|
}
|
|
1069
1434
|
async semanticTree(_opts) {
|
|
@@ -1335,6 +1700,10 @@ var PuppeteerPage = class {
|
|
|
1335
1700
|
active: p === this.page
|
|
1336
1701
|
}));
|
|
1337
1702
|
}
|
|
1703
|
+
async find(query, options) {
|
|
1704
|
+
const elements = await this.page.evaluate(INTERACTIVE_ELEMENTS_SCRIPT);
|
|
1705
|
+
return semanticFind(elements, query, options);
|
|
1706
|
+
}
|
|
1338
1707
|
async close() {
|
|
1339
1708
|
await this.page.close();
|
|
1340
1709
|
}
|