@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.
@@ -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
- // node_modules/sharp/lib/is.js
10070
- var require_is = __commonJS((exports, module) => {
10071
- /*!
10072
- Copyright 2013 Lovell Fuller and others.
10073
- SPDX-License-Identifier: Apache-2.0
10074
- */
10075
- var defined = (val) => typeof val !== "undefined" && val !== null;
10076
- var object = (val) => typeof val === "object";
10077
- var plainObject = (val) => Object.prototype.toString.call(val) === "[object Object]";
10078
- var fn = (val) => typeof val === "function";
10079
- var bool = (val) => typeof val === "boolean";
10080
- var buffer = (val) => val instanceof Buffer;
10081
- var typedArray = (val) => {
10082
- if (defined(val)) {
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
- // node_modules/detect-libc/lib/process.js
10129
- var require_process = __commonJS((exports, module) => {
10130
- var isLinux = () => process.platform === "linux";
10131
- var report = null;
10132
- var getReport = () => {
10133
- if (!report) {
10134
- if (isLinux() && process.report) {
10135
- const orig = process.report.excludeNetwork;
10136
- process.report.excludeNetwork = true;
10137
- report = process.report.getReport();
10138
- process.report.excludeNetwork = orig;
10139
- } else {
10140
- report = {};
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
- return report;
10144
- };
10145
- module.exports = { isLinux, getReport };
10146
- });
10283
+ }
10284
+ const unmatchedRefs = Object.entries(refs).filter(([ref]) => !annotatedTree.includes(`[${ref}]`));
10285
+ if (unmatchedRefs.length > 0) {
10286
+ annotatedTree += `
10147
10287
 
10148
- // node_modules/detect-libc/lib/filesystem.js
10149
- var require_filesystem = __commonJS((exports, module) => {
10150
- var fs = __require("fs");
10151
- var LDD_PATH = "/usr/bin/ldd";
10152
- var SELF_PATH = "/proc/self/exe";
10153
- var MAX_LENGTH = 2048;
10154
- var readFileSync2 = (path) => {
10155
- const fd = fs.openSync(path, "r");
10156
- const buffer = Buffer.alloc(MAX_LENGTH);
10157
- const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
10158
- fs.close(fd, () => {});
10159
- return buffer.subarray(0, bytesRead);
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
- var readFile = (path) => new Promise((resolve, reject) => {
10162
- fs.open(path, "r", (err, fd) => {
10163
- if (err) {
10164
- reject(err);
10165
- } else {
10166
- const buffer = Buffer.alloc(MAX_LENGTH);
10167
- fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
10168
- resolve(buffer.subarray(0, bytesRead));
10169
- fs.close(fd, () => {});
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
- if (elf.readUInt8(5) !== 1) {
10195
- return null;
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
- const offset = elf.readUInt32LE(32);
10198
- const size = elf.readUInt16LE(54);
10199
- const count = elf.readUInt16LE(56);
10200
- for (let i = 0;i < count; i++) {
10201
- const headerOffset = offset + i * size;
10202
- const type2 = elf.readUInt32LE(headerOffset);
10203
- if (type2 === 3) {
10204
- const fileOffset = elf.readUInt32LE(headerOffset + 8);
10205
- const fileSize = elf.readUInt32LE(headerOffset + 32);
10206
- return elf.subarray(fileOffset, fileOffset + fileSize).toString().replace(/\0.*$/g, "");
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
- return null;
10210
- };
10211
- module.exports = {
10212
- interpreterPath
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 __getOwnPropDesc = Object.getOwnPropertyDescriptor;
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 = __getOwnPropDesc(from, key)) || desc.enumerable });
14614
+ __defProp3(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
13740
14615
  }
13741
14616
  return to;
13742
14617
  };
13743
- var __toCommonJS = (mod) => __copyProps(__defProp3({}, "__esModule", { value: true }), mod);
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 = __toCommonJS(index_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.push(entry);
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 loc = page.locator(selector).first();
17803
- if (await loc.count() > 0) {
17804
- return { found: true, locator: loc, method: "original", healed: false, attempts };
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 page.click(selector, {
17861
- button: opts?.button ?? "left",
17862
- clickCount: opts?.clickCount ?? 1,
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
- if (opts?.clear) {
17887
- await page.fill(selector, "", { timeout: opts?.timeout ?? 1e4 });
17888
- }
17889
- await page.type(selector, text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
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
- async function navigate(page, url, timeout = 30000) {
17913
- try {
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
- var activeWatches = new Map;
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) {