pi-lean-dimension 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/README.md +96 -36
- package/node_modules/pi-lean-portal/AGENTS.md +146 -4
- package/node_modules/pi-lean-portal/README.md +112 -50
- package/node_modules/pi-lean-portal/__tests__/browser-data.test.ts +124 -0
- package/node_modules/pi-lean-portal/__tests__/browser-inspect.test.ts +192 -16
- package/node_modules/pi-lean-portal/__tests__/browser-toggle-profile.test.ts +3 -3
- package/node_modules/pi-lean-portal/__tests__/browser-toggle.test.ts +49 -35
- package/node_modules/pi-lean-portal/__tests__/chromium-py-persistence.test.ts +21 -195
- package/node_modules/pi-lean-portal/__tests__/chromium-py.test.ts +17 -81
- package/node_modules/pi-lean-portal/__tests__/chromium.test.ts +25 -0
- package/node_modules/pi-lean-portal/__tests__/contributed/invisible-py/invisible-py.test.ts +299 -0
- package/node_modules/pi-lean-portal/__tests__/cookie-persistence.test.ts +22 -182
- package/node_modules/pi-lean-portal/__tests__/fetch-backend.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/firefox-py-persistence.test.ts +21 -184
- package/node_modules/pi-lean-portal/__tests__/firefox-py.test.ts +17 -101
- package/node_modules/pi-lean-portal/__tests__/firefox.test.ts +2 -18
- package/node_modules/pi-lean-portal/__tests__/helpers/__pycache__/mock-python-bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/__tests__/helpers/create-py-backend-harness.ts +105 -0
- package/node_modules/pi-lean-portal/__tests__/helpers/load-plugin-config-from-file.ts +53 -0
- package/node_modules/pi-lean-portal/__tests__/helpers/mock-plugin.ts +11 -7
- package/node_modules/pi-lean-portal/__tests__/helpers/mock-python-bridge.py +4 -0
- package/node_modules/pi-lean-portal/__tests__/helpers/persistence-suite.ts +218 -0
- package/node_modules/pi-lean-portal/__tests__/helpers/plugin-contract.ts +198 -318
- package/node_modules/pi-lean-portal/__tests__/helpers/probe-user-backend.ts +198 -0
- package/node_modules/pi-lean-portal/__tests__/helpers/test-server.ts +14 -0
- package/node_modules/pi-lean-portal/__tests__/plugin-config-browser.test.ts +150 -15
- package/node_modules/pi-lean-portal/__tests__/plugin-loading.test.ts +120 -18
- package/node_modules/pi-lean-portal/__tests__/plugin-registry.test.ts +6 -67
- package/node_modules/pi-lean-portal/__tests__/probe-user-backend.test.ts +236 -0
- package/node_modules/pi-lean-portal/__tests__/python-adapter.test.ts +401 -11
- package/node_modules/pi-lean-portal/__tests__/router-session.test.ts +4 -1
- package/node_modules/pi-lean-portal/__tests__/run-contributed-suites.test.ts +318 -0
- package/node_modules/pi-lean-portal/__tests__/session-manager.test.ts +50 -0
- package/node_modules/pi-lean-portal/__tests__/snapshot-cache.test.ts +2 -2
- package/node_modules/pi-lean-portal/__tests__/url-safety.test.ts +1 -1
- package/node_modules/pi-lean-portal/backends/chromium/index.ts +5 -13
- package/node_modules/pi-lean-portal/backends/chromium-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/chromium-py/bridge.py +0 -2
- package/node_modules/pi-lean-portal/backends/firefox/index.ts +6 -5
- package/node_modules/pi-lean-portal/backends/firefox-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/firefox-py/bridge.py +7 -6
- package/node_modules/pi-lean-portal/backends/playwright-base/playwright-plugin.ts +241 -398
- package/node_modules/pi-lean-portal/backends/python-adapter.ts +182 -83
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__init__.py +1 -42
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/accessibility.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/accessibility.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bot_detection.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bot_detection.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/browser_data.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/browser_data.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/patch_playwright.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/patch_playwright.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/transport.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/transport.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/accessibility.py +12 -147
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bot_detection.py +12 -37
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bridge.py +249 -322
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/browser_data.py +92 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/patch_playwright.py +321 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/playwright_base.py +511 -299
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/{test_accessibility.cpython-313-pytest-9.1.0.pyc → test_accessibility.cpython-313-pytest-9.1.1.pyc} +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/{test_chromium_py_bridge.cpython-313-pytest-9.1.0.pyc → test_chromium_py_bridge.cpython-313-pytest-9.1.1.pyc} +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/{test_firefox_py_bridge.cpython-313-pytest-9.1.0.pyc → test_firefox_py_bridge.cpython-313-pytest-9.1.1.pyc} +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_py_bridges.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/{test_transport.cpython-313-pytest-9.1.0.pyc → test_transport.cpython-313-pytest-9.1.1.pyc} +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/conftest.py +95 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_accessibility.py +8 -132
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_bot_detection.py +8 -147
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_browser_data.py +131 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_playwright_base_quirks.py +768 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_py_bridges.py +198 -0
- package/node_modules/pi-lean-portal/browser-toggle.ts +33 -69
- package/node_modules/pi-lean-portal/contributed/CHOOSING.md +126 -0
- package/node_modules/pi-lean-portal/contributed/README.md +304 -0
- package/node_modules/pi-lean-portal/contributed/camoufox-py/bridge.py +216 -0
- package/node_modules/pi-lean-portal/contributed/invisible-py/__pycache__/bridge.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/contributed/invisible-py/bridge.py +434 -0
- package/node_modules/pi-lean-portal/core/fetch-backend.ts +0 -5
- package/node_modules/pi-lean-portal/core/plugin-api.ts +6 -33
- package/node_modules/pi-lean-portal/core/plugin-config.ts +75 -58
- package/node_modules/pi-lean-portal/core/plugin-registry.ts +11 -49
- package/node_modules/pi-lean-portal/core/router.ts +59 -98
- package/node_modules/pi-lean-portal/core/shared/accessibility-tree.ts +10 -143
- package/node_modules/pi-lean-portal/core/shared/bot-detection.ts +31 -77
- package/node_modules/pi-lean-portal/core/shared/browser-data.json +183 -0
- package/node_modules/pi-lean-portal/core/shared/browser-data.ts +50 -0
- package/node_modules/pi-lean-portal/core/shared/browser-events.ts +6 -6
- package/node_modules/pi-lean-portal/core/shared/dom-extractor.ts +97 -36
- package/node_modules/pi-lean-portal/core/shared/nav-settle.ts +12 -15
- package/node_modules/pi-lean-portal/core/shared/paths.ts +3 -0
- package/node_modules/pi-lean-portal/core/shared/session-manager.ts +7 -21
- package/node_modules/pi-lean-portal/{verify-ship-manifest.ts → core/shared/ship-manifest.ts} +34 -9
- package/node_modules/pi-lean-portal/core/shared/snapshot-cache.ts +7 -6
- package/node_modules/pi-lean-portal/core/shared/storage-state.ts +40 -9
- package/node_modules/pi-lean-portal/index.ts +42 -7
- package/node_modules/pi-lean-portal/package.json +8 -3
- package/node_modules/pi-lean-portal/ship-manifest.test.ts +8 -3
- package/node_modules/pi-lean-portal/tools/browser-inspect.ts +2 -6
- package/node_modules/pi-lean-portal/tools/browser-navigate.ts +7 -5
- package/node_modules/pi-lean-portal/tools/browser-snapshot.ts +2 -5
- package/node_modules/pi-lean-portal/tools/utils.ts +22 -4
- package/node_modules/pi-lean-portal/tools/web-fetch.ts +3 -4
- package/node_modules/pi-lean-search/README.md +6 -2
- package/node_modules/pi-lean-search/__tests__/web-search.test.ts +11 -0
- package/node_modules/pi-lean-search/index.ts +4 -21
- package/node_modules/pi-lean-search/package.json +2 -2
- package/node_modules/pi-lean-search/verify-ship-manifest.ts +6 -92
- package/node_modules/pi-lean-search/web-search-tool.ts +19 -13
- package/package.json +4 -4
- package/node_modules/pi-lean-portal/__tests__/helpers/reddit-fixture.ts +0 -264
- package/node_modules/pi-lean-portal/__tests__/helpers/toggle-test-utils.ts +0 -31
- package/node_modules/pi-lean-portal/__tests__/reddit-dialog.test.ts +0 -302
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/occlusion.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313-pytest-9.1.0.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_chromium_py_bridge.py +0 -281
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_firefox_py_bridge.py +0 -212
|
@@ -27,9 +27,10 @@ import {
|
|
|
27
27
|
import { sessionManager } from "../../core/shared/session-manager.js";
|
|
28
28
|
import { checkPage } from "../../core/shared/bot-detection.js";
|
|
29
29
|
import { waitForNavigationSettle } from "../../core/shared/nav-settle.js";
|
|
30
|
+
import { NAV_SETTLE } from "../../core/shared/browser-data.js";
|
|
30
31
|
import { join } from "node:path";
|
|
31
32
|
import { mkdirSync } from "node:fs";
|
|
32
|
-
import {
|
|
33
|
+
import { persistSessionState } from "../../core/shared/storage-state.js";
|
|
33
34
|
import type {
|
|
34
35
|
BrowserPlugin,
|
|
35
36
|
PluginCapabilities,
|
|
@@ -64,7 +65,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
64
65
|
/** Unique stable identifier (e.g. "chromium", "firefox") */
|
|
65
66
|
abstract readonly name: string;
|
|
66
67
|
|
|
67
|
-
/** Advertised capabilities */
|
|
68
68
|
abstract readonly capabilities: PluginCapabilities;
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -95,18 +95,38 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
95
95
|
*/
|
|
96
96
|
protected readonly captureUserAgent: boolean = false;
|
|
97
97
|
|
|
98
|
-
// ── Private state ──────────────────────────────────────────
|
|
99
|
-
|
|
100
|
-
/** Enable structured debug logging via BROWSER_DEBUG env var */
|
|
101
98
|
private readonly _debug = process.env.BROWSER_DEBUG === "1";
|
|
102
99
|
|
|
103
|
-
/** Log a structured debug line to stderr when BROWSER_DEBUG=1 */
|
|
104
100
|
private _log(event: string, data: Record<string, unknown>): void {
|
|
105
101
|
if (this._debug) {
|
|
106
102
|
process.stderr.write(`[browser] ${event}: ${JSON.stringify(data)}\n`);
|
|
107
103
|
}
|
|
108
104
|
}
|
|
109
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Wrap an async operation with automatic success/failure debug logging.
|
|
108
|
+
* Runs the body, then logs based on the result. Body should catch its own
|
|
109
|
+
* errors and return `{ success: false, error }` — an uncaught throw skips
|
|
110
|
+
* the log (but shouldn't happen in normal operation).
|
|
111
|
+
*/
|
|
112
|
+
private async _logOp<T extends { success: boolean; error?: string }>(
|
|
113
|
+
op: string,
|
|
114
|
+
ctx: Record<string, unknown>,
|
|
115
|
+
body: () => Promise<T>,
|
|
116
|
+
getExtra?: (result: T) => Record<string, unknown>,
|
|
117
|
+
): Promise<T> {
|
|
118
|
+
const result = await body();
|
|
119
|
+
if (this._debug) {
|
|
120
|
+
this._log(op, {
|
|
121
|
+
...ctx,
|
|
122
|
+
...(getExtra ? getExtra(result) : {}),
|
|
123
|
+
success: result.success,
|
|
124
|
+
...(result.error ? { error: result.error } : {}),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
/** Shared browser instance (lazy-initialised) */
|
|
111
131
|
private _browser: Browser | null = null;
|
|
112
132
|
|
|
@@ -273,15 +293,22 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
273
293
|
if (!this._browser) {
|
|
274
294
|
this._browser = await this._launchWithHint();
|
|
275
295
|
|
|
276
|
-
// Auto-recover from browser crash/disconnect
|
|
277
|
-
|
|
278
|
-
|
|
296
|
+
// Auto-recover from browser crash/disconnect.
|
|
297
|
+
// Extracted as a named function for readability. On disconnect:
|
|
298
|
+
// mark all sessions crashed, clear caches, and null `_browser`
|
|
299
|
+
// so the next navigate() relaunches via lazy-init (which attaches
|
|
300
|
+
// a fresh handler to the new browser).
|
|
301
|
+
const onDisconnected = () => {
|
|
302
|
+
// All Page/Context objects are dead once the Browser
|
|
303
|
+
// disconnects — mark sessions crashed and clear caches.
|
|
279
304
|
for (const tid of this._pages.keys()) {
|
|
280
305
|
sessionManager.updateSession(tid, { crashed: true });
|
|
281
306
|
this._elementCache.delete(tid);
|
|
282
307
|
}
|
|
283
308
|
this._pages.clear();
|
|
284
|
-
|
|
309
|
+
this._browser = null;
|
|
310
|
+
};
|
|
311
|
+
this._browser.on("disconnected", onDisconnected);
|
|
285
312
|
|
|
286
313
|
// UA capture at first launch (Firefox opt-in)
|
|
287
314
|
if (this.captureUserAgent) {
|
|
@@ -395,25 +422,9 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
395
422
|
context: BrowserContext,
|
|
396
423
|
): Promise<{ cookies: unknown[]; origins: unknown[] } | undefined> {
|
|
397
424
|
const session = sessionManager.getSession(taskId);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
try {
|
|
401
|
-
const state = await context.storageState();
|
|
402
|
-
const name = session.profileName ?? "default";
|
|
403
|
-
saveStorageState(name, state);
|
|
404
|
-
return state;
|
|
405
|
-
} catch (err) {
|
|
406
|
-
console.warn(
|
|
407
|
-
`[pi-lean-portal] Failed to auto-save storage state for profile ` +
|
|
408
|
-
`'${session.profileName ?? "default"}': ` +
|
|
409
|
-
`${err instanceof Error ? err.message : String(err)}. ` +
|
|
410
|
-
"Session state may be lost.",
|
|
411
|
-
);
|
|
412
|
-
return undefined;
|
|
413
|
-
}
|
|
425
|
+
return persistSessionState(session, () => context.storageState());
|
|
414
426
|
}
|
|
415
427
|
|
|
416
|
-
/** Take an accessibility snapshot and update the element cache. */
|
|
417
428
|
private async takeSnapshot(
|
|
418
429
|
taskId: string,
|
|
419
430
|
page: Page,
|
|
@@ -465,12 +476,12 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
465
476
|
private async checkBotDetection(page: Page): Promise<boolean> {
|
|
466
477
|
try {
|
|
467
478
|
const title = await page.title();
|
|
468
|
-
const bodyText = await page.evaluate(
|
|
469
|
-
|
|
479
|
+
const bodyText: string = await page.evaluate(
|
|
480
|
+
"document.body?.innerText || ''",
|
|
470
481
|
);
|
|
471
482
|
// Also grab raw HTML to check for CAPTCHA widget embed codes.
|
|
472
|
-
const html = await page.evaluate(
|
|
473
|
-
|
|
483
|
+
const html: string = await page.evaluate(
|
|
484
|
+
"document.documentElement?.innerHTML || ''",
|
|
474
485
|
);
|
|
475
486
|
// checkPage handles all three: title (challenge phrases),
|
|
476
487
|
// body (challenge phrases + CDN patterns), and HTML (CAPTCHA embeds).
|
|
@@ -493,7 +504,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
493
504
|
profileMode?: "none" | "session" | "named";
|
|
494
505
|
},
|
|
495
506
|
): Promise<NavigateResult> {
|
|
496
|
-
const _start = performance.now();
|
|
497
507
|
try {
|
|
498
508
|
const ctxOpts: {
|
|
499
509
|
storageState?: unknown;
|
|
@@ -546,19 +556,9 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
546
556
|
|
|
547
557
|
// Wait for dynamic content to settle
|
|
548
558
|
try {
|
|
549
|
-
await page.waitForFunction(
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const count = document.querySelectorAll("*").length;
|
|
553
|
-
setTimeout(() => {
|
|
554
|
-
resolve(
|
|
555
|
-
document.querySelectorAll("*").length === count ||
|
|
556
|
-
count > 5000,
|
|
557
|
-
);
|
|
558
|
-
}, 400);
|
|
559
|
-
}),
|
|
560
|
-
{ timeout: 5000 },
|
|
561
|
-
);
|
|
559
|
+
await page.waitForFunction(NAV_SETTLE.domStabilizationJs, {
|
|
560
|
+
timeout: NAV_SETTLE.navTimeoutMs,
|
|
561
|
+
});
|
|
562
562
|
} catch {
|
|
563
563
|
// Stabilization timed out — proceed with whatever is rendered
|
|
564
564
|
}
|
|
@@ -589,7 +589,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
589
589
|
success: true,
|
|
590
590
|
botDetected: botDetected ?? false,
|
|
591
591
|
elementCount,
|
|
592
|
-
time: Math.round(performance.now() - _start),
|
|
593
592
|
});
|
|
594
593
|
|
|
595
594
|
return {
|
|
@@ -632,7 +631,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
632
631
|
botDetected: botDetected ?? false,
|
|
633
632
|
elementCount: 0,
|
|
634
633
|
error: msg,
|
|
635
|
-
time: Math.round(performance.now() - _start),
|
|
636
634
|
});
|
|
637
635
|
|
|
638
636
|
return {
|
|
@@ -648,7 +646,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
648
646
|
}
|
|
649
647
|
|
|
650
648
|
async snapshot(taskId: string): Promise<SnapshotResult> {
|
|
651
|
-
const _start = performance.now();
|
|
652
649
|
const page = this.requirePage(taskId, "snapshot");
|
|
653
650
|
if (!page) {
|
|
654
651
|
return {
|
|
@@ -659,51 +656,24 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
659
656
|
};
|
|
660
657
|
}
|
|
661
658
|
|
|
662
|
-
|
|
659
|
+
return this._logOp("snapshot", { taskId }, async () => {
|
|
663
660
|
const {
|
|
664
661
|
snapshot: snapText,
|
|
665
662
|
elementCount,
|
|
666
663
|
dialogEvents,
|
|
667
664
|
} = await this.takeSnapshot(taskId, page);
|
|
668
|
-
|
|
669
|
-
this._log("snapshot", {
|
|
670
|
-
taskId,
|
|
671
|
-
success: true,
|
|
672
|
-
elementCount,
|
|
673
|
-
dialogEvents: dialogEvents.length,
|
|
674
|
-
fingerprint: snapText.slice(0, 16),
|
|
675
|
-
time: Math.round(performance.now() - _start),
|
|
676
|
-
});
|
|
677
|
-
|
|
678
665
|
return {
|
|
679
666
|
success: true,
|
|
680
667
|
snapshot: snapText,
|
|
681
668
|
elementCount,
|
|
682
669
|
dialogEvents,
|
|
683
670
|
};
|
|
684
|
-
}
|
|
685
|
-
this._log("snapshot", {
|
|
686
|
-
taskId,
|
|
687
|
-
success: false,
|
|
688
|
-
elementCount: 0,
|
|
689
|
-
error: err instanceof Error ? err.message : String(err),
|
|
690
|
-
time: Math.round(performance.now() - _start),
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
return {
|
|
694
|
-
success: false,
|
|
695
|
-
snapshot: "",
|
|
696
|
-
elementCount: 0,
|
|
697
|
-
error: err instanceof Error ? err.message : String(err),
|
|
698
|
-
};
|
|
699
|
-
}
|
|
671
|
+
}, (r) => ({ elementCount: r.elementCount, dialogEvents: r.dialogEvents.length, fingerprint: r.snapshot.slice(0, 16) }));
|
|
700
672
|
}
|
|
701
673
|
|
|
702
674
|
// ── Interaction ────────────────────────────────────────────
|
|
703
675
|
|
|
704
676
|
async click(taskId: string, ref: string): Promise<InteractionResult> {
|
|
705
|
-
const _start = performance.now();
|
|
706
|
-
const phases: Record<string, number> = {};
|
|
707
677
|
const page = this.requirePage(taskId, "click");
|
|
708
678
|
if (!page) {
|
|
709
679
|
return { success: false, error: "No active session" };
|
|
@@ -720,7 +690,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
720
690
|
name: "(none)",
|
|
721
691
|
result: "fail",
|
|
722
692
|
error: `Element ${ref} not found in accessibility tree`,
|
|
723
|
-
time: Math.round(performance.now() - _start),
|
|
724
693
|
});
|
|
725
694
|
return {
|
|
726
695
|
success: false,
|
|
@@ -737,71 +706,46 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
737
706
|
name: node.name,
|
|
738
707
|
result: "fail",
|
|
739
708
|
error: `Could not build locator (role: ${node.role})`,
|
|
740
|
-
time: Math.round(performance.now() - _start),
|
|
741
709
|
});
|
|
742
710
|
return {
|
|
743
711
|
success: false,
|
|
744
712
|
error: `Could not build locator for ${ref} (role: ${node.role})`,
|
|
745
713
|
};
|
|
746
714
|
}
|
|
747
|
-
phases.locate = Math.round(performance.now() - _start);
|
|
748
|
-
|
|
749
|
-
try {
|
|
750
|
-
const urlBefore = page.url();
|
|
751
|
-
await locator.click({ timeout: 5000 });
|
|
752
|
-
phases.click = Math.round(performance.now() - _start);
|
|
753
|
-
|
|
754
|
-
// Wait for potential navigation to settle (replaces fixed sleep)
|
|
755
|
-
const { navigated } = await waitForNavigationSettle(page, urlBefore);
|
|
756
|
-
phases.wait = Math.round(performance.now() - _start);
|
|
757
|
-
|
|
758
|
-
const newUrl = page.url();
|
|
759
|
-
const newTitle = await page.title();
|
|
760
|
-
sessionManager.updateSession(taskId, {
|
|
761
|
-
currentUrl: newUrl,
|
|
762
|
-
currentTitle: newTitle,
|
|
763
|
-
});
|
|
764
715
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
716
|
+
return this._logOp("click", { taskId, ref, role: node.role, name: node.name }, async () => {
|
|
717
|
+
try {
|
|
718
|
+
const urlBefore = page.url();
|
|
719
|
+
await locator.click({ timeout: 5000 });
|
|
768
720
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
ref,
|
|
772
|
-
role: node.role,
|
|
773
|
-
name: node.name,
|
|
774
|
-
result: "success",
|
|
775
|
-
navigated,
|
|
776
|
-
timings: phases,
|
|
777
|
-
time: Math.round(performance.now() - _start),
|
|
778
|
-
});
|
|
721
|
+
// Wait for potential navigation to settle (replaces fixed sleep)
|
|
722
|
+
const { navigated: _navigated } = await waitForNavigationSettle(page, urlBefore);
|
|
779
723
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
dialogEvents: snapResult.dialogEvents,
|
|
787
|
-
};
|
|
788
|
-
} catch (err: unknown) {
|
|
789
|
-
this._log("click", {
|
|
790
|
-
taskId,
|
|
791
|
-
ref,
|
|
792
|
-
role: node.role,
|
|
793
|
-
name: node.name,
|
|
794
|
-
result: "fail",
|
|
795
|
-
error: err instanceof Error ? err.message : String(err),
|
|
796
|
-
timings: phases,
|
|
797
|
-
time: Math.round(performance.now() - _start),
|
|
798
|
-
});
|
|
724
|
+
const newUrl = page.url();
|
|
725
|
+
const newTitle = await page.title();
|
|
726
|
+
sessionManager.updateSession(taskId, {
|
|
727
|
+
currentUrl: newUrl,
|
|
728
|
+
currentTitle: newTitle,
|
|
729
|
+
});
|
|
799
730
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
731
|
+
// Auto-snapshot
|
|
732
|
+
const snapResult = await this.takeSnapshot(taskId, page);
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
success: true,
|
|
736
|
+
newUrl,
|
|
737
|
+
newTitle,
|
|
738
|
+
snapshot: snapResult.snapshot,
|
|
739
|
+
elementCount: snapResult.elementCount,
|
|
740
|
+
dialogEvents: snapResult.dialogEvents,
|
|
741
|
+
};
|
|
742
|
+
} catch (err: unknown) {
|
|
743
|
+
return {
|
|
744
|
+
success: false,
|
|
745
|
+
error: `Click failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
});
|
|
805
749
|
}
|
|
806
750
|
|
|
807
751
|
async type(
|
|
@@ -809,7 +753,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
809
753
|
ref: string,
|
|
810
754
|
text: string,
|
|
811
755
|
): Promise<InteractionResult> {
|
|
812
|
-
const _start = performance.now();
|
|
813
756
|
const page = this.requirePage(taskId, "type");
|
|
814
757
|
if (!page) {
|
|
815
758
|
return { success: false, error: "No active session" };
|
|
@@ -826,7 +769,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
826
769
|
name: "(none)",
|
|
827
770
|
result: "fail",
|
|
828
771
|
error: `Element ${ref} not found in accessibility tree`,
|
|
829
|
-
time: Math.round(performance.now() - _start),
|
|
830
772
|
});
|
|
831
773
|
return {
|
|
832
774
|
success: false,
|
|
@@ -843,7 +785,6 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
843
785
|
name: node.name,
|
|
844
786
|
result: "fail",
|
|
845
787
|
error: `Could not build locator (role: ${node.role})`,
|
|
846
|
-
time: Math.round(performance.now() - _start),
|
|
847
788
|
});
|
|
848
789
|
return {
|
|
849
790
|
success: false,
|
|
@@ -851,199 +792,141 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
851
792
|
};
|
|
852
793
|
}
|
|
853
794
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
dialogEvents: snapResult.dialogEvents,
|
|
876
|
-
};
|
|
877
|
-
} catch (err: unknown) {
|
|
878
|
-
this._log("type", {
|
|
879
|
-
taskId,
|
|
880
|
-
ref,
|
|
881
|
-
role: node.role,
|
|
882
|
-
name: node.name,
|
|
883
|
-
result: "fail",
|
|
884
|
-
error: err instanceof Error ? err.message : String(err),
|
|
885
|
-
time: Math.round(performance.now() - _start),
|
|
886
|
-
});
|
|
887
|
-
return {
|
|
888
|
-
success: false,
|
|
889
|
-
error: `Type failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
890
|
-
};
|
|
891
|
-
}
|
|
795
|
+
return this._logOp("type", { taskId, ref, role: node.role, name: node.name }, async () => {
|
|
796
|
+
try {
|
|
797
|
+
await locator.click({ timeout: 5000 }); // Focus first
|
|
798
|
+
await locator.fill(text);
|
|
799
|
+
|
|
800
|
+
// Auto-snapshot
|
|
801
|
+
const snapResult = await this.takeSnapshot(taskId, page);
|
|
802
|
+
|
|
803
|
+
return {
|
|
804
|
+
success: true,
|
|
805
|
+
snapshot: snapResult.snapshot,
|
|
806
|
+
elementCount: snapResult.elementCount,
|
|
807
|
+
dialogEvents: snapResult.dialogEvents,
|
|
808
|
+
};
|
|
809
|
+
} catch (err: unknown) {
|
|
810
|
+
return {
|
|
811
|
+
success: false,
|
|
812
|
+
error: `Type failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
}, (r) => ({ elementCount: r.elementCount }));
|
|
892
816
|
}
|
|
893
817
|
|
|
894
818
|
async scroll(
|
|
895
819
|
taskId: string,
|
|
896
820
|
direction: "up" | "down",
|
|
897
821
|
): Promise<InteractionResult> {
|
|
898
|
-
const _start = performance.now();
|
|
899
822
|
const page = this.requirePage(taskId, "scroll");
|
|
900
823
|
if (!page) {
|
|
901
824
|
return { success: false, error: "No active session" };
|
|
902
825
|
}
|
|
903
826
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
} catch (err: unknown) {
|
|
928
|
-
this._log("scroll", {
|
|
929
|
-
taskId,
|
|
930
|
-
direction,
|
|
931
|
-
success: false,
|
|
932
|
-
error: err instanceof Error ? err.message : String(err),
|
|
933
|
-
time: Math.round(performance.now() - _start),
|
|
934
|
-
});
|
|
935
|
-
return {
|
|
936
|
-
success: false,
|
|
937
|
-
error: `Scroll failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
938
|
-
};
|
|
939
|
-
}
|
|
827
|
+
return this._logOp("scroll", { taskId, direction }, async () => {
|
|
828
|
+
try {
|
|
829
|
+
const delta = direction === "down" ? 800 : -800;
|
|
830
|
+
await page.evaluate(
|
|
831
|
+
`window.scrollBy({ top: ${delta}, behavior: "smooth" })`,
|
|
832
|
+
);
|
|
833
|
+
await page.waitForTimeout(200);
|
|
834
|
+
|
|
835
|
+
const snapResult = await this.takeSnapshot(taskId, page);
|
|
836
|
+
|
|
837
|
+
return {
|
|
838
|
+
success: true,
|
|
839
|
+
snapshot: snapResult.snapshot,
|
|
840
|
+
elementCount: snapResult.elementCount,
|
|
841
|
+
dialogEvents: snapResult.dialogEvents,
|
|
842
|
+
};
|
|
843
|
+
} catch (err: unknown) {
|
|
844
|
+
return {
|
|
845
|
+
success: false,
|
|
846
|
+
error: `Scroll failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
}, (r) => ({ elementCount: r.elementCount }));
|
|
940
850
|
}
|
|
941
851
|
|
|
942
852
|
async goBack(taskId: string): Promise<InteractionResult> {
|
|
943
|
-
const _start = performance.now();
|
|
944
853
|
const page = this.requirePage(taskId, "goBack");
|
|
945
854
|
if (!page) {
|
|
946
855
|
return { success: false, error: "No active session" };
|
|
947
856
|
}
|
|
948
857
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
const snapResult = await this.takeSnapshot(taskId, page);
|
|
961
|
-
|
|
962
|
-
this._log("goBack", {
|
|
963
|
-
taskId,
|
|
964
|
-
success: true,
|
|
965
|
-
elementCount: snapResult.elementCount,
|
|
966
|
-
time: Math.round(performance.now() - _start),
|
|
967
|
-
});
|
|
858
|
+
return this._logOp("goBack", { taskId }, async () => {
|
|
859
|
+
try {
|
|
860
|
+
await page.goBack({ waitUntil: "networkidle" });
|
|
861
|
+
await page.waitForTimeout(300);
|
|
862
|
+
|
|
863
|
+
const newUrl = page.url();
|
|
864
|
+
const newTitle = await page.title();
|
|
865
|
+
sessionManager.updateSession(taskId, {
|
|
866
|
+
currentUrl: newUrl,
|
|
867
|
+
currentTitle: newTitle,
|
|
868
|
+
});
|
|
968
869
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
error: `GoBack failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
987
|
-
};
|
|
988
|
-
}
|
|
870
|
+
const snapResult = await this.takeSnapshot(taskId, page);
|
|
871
|
+
|
|
872
|
+
return {
|
|
873
|
+
success: true,
|
|
874
|
+
newUrl,
|
|
875
|
+
newTitle,
|
|
876
|
+
snapshot: snapResult.snapshot,
|
|
877
|
+
elementCount: snapResult.elementCount,
|
|
878
|
+
dialogEvents: snapResult.dialogEvents,
|
|
879
|
+
};
|
|
880
|
+
} catch (err: unknown) {
|
|
881
|
+
return {
|
|
882
|
+
success: false,
|
|
883
|
+
error: `GoBack failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}, (r) => ({ elementCount: r.elementCount }));
|
|
989
887
|
}
|
|
990
888
|
|
|
991
889
|
async press(taskId: string, key: string): Promise<InteractionResult> {
|
|
992
|
-
const _start = performance.now();
|
|
993
890
|
const page = this.requirePage(taskId, "press");
|
|
994
891
|
if (!page) {
|
|
995
892
|
return { success: false, error: "No active session" };
|
|
996
893
|
}
|
|
997
894
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
// Wait for potential navigation to settle (replaces fixed sleep).
|
|
1003
|
-
// Shorter nav timeout since Enter-on-link nav is typically fast.
|
|
1004
|
-
const { navigated } = await waitForNavigationSettle(page, urlBefore, {
|
|
1005
|
-
navTimeoutMs: 3000,
|
|
1006
|
-
});
|
|
1007
|
-
|
|
1008
|
-
const newUrl = page.url();
|
|
1009
|
-
const newTitle = await page.title();
|
|
1010
|
-
sessionManager.updateSession(taskId, {
|
|
1011
|
-
currentUrl: newUrl,
|
|
1012
|
-
currentTitle: newTitle,
|
|
1013
|
-
});
|
|
895
|
+
return this._logOp("press", { taskId, key }, async () => {
|
|
896
|
+
try {
|
|
897
|
+
const urlBefore = page.url();
|
|
898
|
+
await page.keyboard.press(key);
|
|
1014
899
|
|
|
1015
|
-
|
|
900
|
+
// Wait for potential navigation to settle (replaces fixed sleep).
|
|
901
|
+
// Shorter nav timeout since Enter-on-link nav is typically fast.
|
|
902
|
+
const { navigated: _navigated } = await waitForNavigationSettle(page, urlBefore, {
|
|
903
|
+
navTimeoutMs: 3000,
|
|
904
|
+
});
|
|
1016
905
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
time: Math.round(performance.now() - _start),
|
|
1024
|
-
});
|
|
906
|
+
const newUrl = page.url();
|
|
907
|
+
const newTitle = await page.title();
|
|
908
|
+
sessionManager.updateSession(taskId, {
|
|
909
|
+
currentUrl: newUrl,
|
|
910
|
+
currentTitle: newTitle,
|
|
911
|
+
});
|
|
1025
912
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
success: false,
|
|
1044
|
-
error: `Press failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
913
|
+
const snapResult = await this.takeSnapshot(taskId, page);
|
|
914
|
+
|
|
915
|
+
return {
|
|
916
|
+
success: true,
|
|
917
|
+
newUrl,
|
|
918
|
+
newTitle,
|
|
919
|
+
snapshot: snapResult.snapshot,
|
|
920
|
+
elementCount: snapResult.elementCount,
|
|
921
|
+
dialogEvents: snapResult.dialogEvents,
|
|
922
|
+
};
|
|
923
|
+
} catch (err: unknown) {
|
|
924
|
+
return {
|
|
925
|
+
success: false,
|
|
926
|
+
error: `Press failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
}, (r) => ({ elementCount: r.elementCount }));
|
|
1047
930
|
}
|
|
1048
931
|
|
|
1049
932
|
// ── Media ──────────────────────────────────────────────────
|
|
@@ -1090,7 +973,11 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
1090
973
|
clearConsoleLog(taskId);
|
|
1091
974
|
}
|
|
1092
975
|
|
|
1093
|
-
async evaluate(
|
|
976
|
+
async evaluate(
|
|
977
|
+
taskId: string,
|
|
978
|
+
expression: string,
|
|
979
|
+
_readOnly?: boolean,
|
|
980
|
+
): Promise<EvaluateResult> {
|
|
1094
981
|
const page = this.requirePage(taskId);
|
|
1095
982
|
if (!page) {
|
|
1096
983
|
return { success: false, error: "No active session" };
|
|
@@ -1110,104 +997,71 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
1110
997
|
// ── Cookies & storage state ───────────────────────────────
|
|
1111
998
|
|
|
1112
999
|
async getCookies(taskId: string, urls?: string[]): Promise<CookieResult> {
|
|
1113
|
-
const _start = performance.now();
|
|
1114
1000
|
const entry = this.requireEntry(taskId, "getCookies");
|
|
1115
1001
|
if (!entry) {
|
|
1116
1002
|
return { success: false, cookies: [], error: "No active session" };
|
|
1117
1003
|
}
|
|
1118
1004
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
success: false,
|
|
1132
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1133
|
-
time: Math.round(performance.now() - _start),
|
|
1134
|
-
});
|
|
1135
|
-
return {
|
|
1136
|
-
success: false,
|
|
1137
|
-
cookies: [],
|
|
1138
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1139
|
-
};
|
|
1140
|
-
}
|
|
1005
|
+
return this._logOp("getCookies", { taskId }, async () => {
|
|
1006
|
+
try {
|
|
1007
|
+
const cookies = await entry.context.cookies(urls);
|
|
1008
|
+
return { success: true, cookies };
|
|
1009
|
+
} catch (err: unknown) {
|
|
1010
|
+
return {
|
|
1011
|
+
success: false,
|
|
1012
|
+
cookies: [],
|
|
1013
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
}, (r) => ({ count: r.cookies.length }));
|
|
1141
1017
|
}
|
|
1142
1018
|
|
|
1143
1019
|
async addCookies(taskId: string, cookies: Cookie[]): Promise<ResultBase> {
|
|
1144
|
-
const _start = performance.now();
|
|
1145
1020
|
const entry = this.requireEntry(taskId, "addCookies");
|
|
1146
1021
|
if (!entry) {
|
|
1147
1022
|
return { success: false, error: "No active session" };
|
|
1148
1023
|
}
|
|
1149
1024
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
taskId,
|
|
1162
|
-
success: false,
|
|
1163
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1164
|
-
time: Math.round(performance.now() - _start),
|
|
1165
|
-
});
|
|
1166
|
-
return {
|
|
1167
|
-
success: false,
|
|
1168
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1025
|
+
return this._logOp("addCookies", { taskId }, async () => {
|
|
1026
|
+
try {
|
|
1027
|
+
await entry.context.addCookies(cookies);
|
|
1028
|
+
return { success: true };
|
|
1029
|
+
} catch (err: unknown) {
|
|
1030
|
+
return {
|
|
1031
|
+
success: false,
|
|
1032
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1171
1036
|
}
|
|
1172
1037
|
|
|
1173
1038
|
async clearCookies(
|
|
1174
1039
|
taskId: string,
|
|
1175
1040
|
options?: ClearCookiesOptions,
|
|
1176
1041
|
): Promise<ResultBase> {
|
|
1177
|
-
const _start = performance.now();
|
|
1178
1042
|
const entry = this.requireEntry(taskId, "clearCookies");
|
|
1179
1043
|
if (!entry) {
|
|
1180
1044
|
return { success: false, error: "No active session" };
|
|
1181
1045
|
}
|
|
1182
1046
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
success: false,
|
|
1199
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1200
|
-
time: Math.round(performance.now() - _start),
|
|
1201
|
-
});
|
|
1202
|
-
return {
|
|
1203
|
-
success: false,
|
|
1204
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1047
|
+
return this._logOp("clearCookies", { taskId }, async () => {
|
|
1048
|
+
try {
|
|
1049
|
+
await entry.context.clearCookies({
|
|
1050
|
+
...(options?.name ? { name: options.name } : {}),
|
|
1051
|
+
...(options?.domain ? { domain: options.domain } : {}),
|
|
1052
|
+
...(options?.path ? { path: options.path } : {}),
|
|
1053
|
+
});
|
|
1054
|
+
return { success: true };
|
|
1055
|
+
} catch (err: unknown) {
|
|
1056
|
+
return {
|
|
1057
|
+
success: false,
|
|
1058
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1207
1062
|
}
|
|
1208
1063
|
|
|
1209
1064
|
async getStorageState(taskId: string): Promise<StorageStateResult> {
|
|
1210
|
-
const _start = performance.now();
|
|
1211
1065
|
const entry = this.requireEntry(taskId, "getStorageState");
|
|
1212
1066
|
if (!entry) {
|
|
1213
1067
|
return {
|
|
@@ -1218,34 +1072,23 @@ export abstract class PlaywrightPluginBase implements BrowserPlugin {
|
|
|
1218
1072
|
};
|
|
1219
1073
|
}
|
|
1220
1074
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
})
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
success: false,
|
|
1239
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1240
|
-
time: Math.round(performance.now() - _start),
|
|
1241
|
-
});
|
|
1242
|
-
return {
|
|
1243
|
-
success: false,
|
|
1244
|
-
cookies: [],
|
|
1245
|
-
origins: [],
|
|
1246
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1247
|
-
};
|
|
1248
|
-
}
|
|
1075
|
+
return this._logOp("getStorageState", { taskId }, async () => {
|
|
1076
|
+
try {
|
|
1077
|
+
const state = await entry.context.storageState();
|
|
1078
|
+
return {
|
|
1079
|
+
success: true,
|
|
1080
|
+
cookies: state.cookies,
|
|
1081
|
+
origins: state.origins,
|
|
1082
|
+
};
|
|
1083
|
+
} catch (err: unknown) {
|
|
1084
|
+
return {
|
|
1085
|
+
success: false,
|
|
1086
|
+
cookies: [],
|
|
1087
|
+
origins: [],
|
|
1088
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
}, (r) => ({ cookies: r.cookies.length, origins: r.origins.length }));
|
|
1249
1092
|
}
|
|
1250
1093
|
|
|
1251
1094
|
// ── Per-task cleanup ───────────────────────────────────────
|