browxai 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/THIRD_PARTY_NOTICES.md +45 -0
- package/dist/cli/chrome.d.ts +1 -0
- package/dist/cli/chrome.js +130 -0
- package/dist/cli/command-registry.d.ts +15 -0
- package/dist/cli/command-registry.js +35 -0
- package/dist/cli/doctor-plugins.d.ts +18 -0
- package/dist/cli/doctor-plugins.js +338 -0
- package/dist/cli/doctor.d.ts +9 -0
- package/dist/cli/doctor.js +407 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +200 -0
- package/dist/cli/register-commands.d.ts +1 -0
- package/dist/cli/register-commands.js +22 -0
- package/dist/cli/serve.d.ts +14 -0
- package/dist/cli/serve.js +151 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +129 -0
- package/dist/engine/adapters/adb.d.ts +72 -0
- package/dist/engine/adapters/adb.js +200 -0
- package/dist/engine/adapters/android-cdp.d.ts +54 -0
- package/dist/engine/adapters/android-cdp.js +110 -0
- package/dist/engine/adapters/android.engine.d.ts +1 -0
- package/dist/engine/adapters/android.engine.js +31 -0
- package/dist/engine/adapters/chromium.engine.d.ts +1 -0
- package/dist/engine/adapters/chromium.engine.js +44 -0
- package/dist/engine/adapters/firefox.engine.d.ts +1 -0
- package/dist/engine/adapters/firefox.engine.js +43 -0
- package/dist/engine/adapters/playwright-chromium.d.ts +43 -0
- package/dist/engine/adapters/playwright-chromium.js +56 -0
- package/dist/engine/adapters/playwright-firefox.d.ts +52 -0
- package/dist/engine/adapters/playwright-firefox.js +97 -0
- package/dist/engine/adapters/playwright-webkit.d.ts +40 -0
- package/dist/engine/adapters/playwright-webkit.js +79 -0
- package/dist/engine/adapters/safari/bidi-client.d.ts +46 -0
- package/dist/engine/adapters/safari/bidi-client.js +130 -0
- package/dist/engine/adapters/safari/launch.d.ts +56 -0
- package/dist/engine/adapters/safari/launch.js +104 -0
- package/dist/engine/adapters/safari/webdriver-client.d.ts +102 -0
- package/dist/engine/adapters/safari/webdriver-client.js +175 -0
- package/dist/engine/adapters/safari.engine.d.ts +1 -0
- package/dist/engine/adapters/safari.engine.js +52 -0
- package/dist/engine/adapters/safaridriver-hybrid.d.ts +56 -0
- package/dist/engine/adapters/safaridriver-hybrid.js +127 -0
- package/dist/engine/adapters/webkit.engine.d.ts +1 -0
- package/dist/engine/adapters/webkit.engine.js +47 -0
- package/dist/engine/capabilities.d.ts +53 -0
- package/dist/engine/capabilities.js +122 -0
- package/dist/engine/capability-registry.d.ts +9 -0
- package/dist/engine/capability-registry.js +20 -0
- package/dist/engine/index.d.ts +18 -0
- package/dist/engine/index.js +14 -0
- package/dist/engine/register-engines.d.ts +5 -0
- package/dist/engine/register-engines.js +16 -0
- package/dist/engine/registry.d.ts +145 -0
- package/dist/engine/registry.js +67 -0
- package/dist/engine/select.d.ts +48 -0
- package/dist/engine/select.js +128 -0
- package/dist/engine/session-cdp.d.ts +13 -0
- package/dist/engine/session-cdp.js +22 -0
- package/dist/engine/tool-gate.d.ts +19 -0
- package/dist/engine/tool-gate.js +226 -0
- package/dist/engine/types.d.ts +71 -0
- package/dist/engine/types.js +16 -0
- package/dist/helper/bridge.d.ts +48 -0
- package/dist/helper/bridge.js +200 -0
- package/dist/helper/browx-page.d.ts +1 -0
- package/dist/helper/browx-page.js +47 -0
- package/dist/helper/overlay-hide.d.ts +9 -0
- package/dist/helper/overlay-hide.js +49 -0
- package/dist/helper/stealth.d.ts +10 -0
- package/dist/helper/stealth.js +88 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +15 -0
- package/dist/page/a11y.d.ts +81 -0
- package/dist/page/a11y.js +219 -0
- package/dist/page/action-substrate.d.ts +64 -0
- package/dist/page/action-substrate.js +118 -0
- package/dist/page/actionresult-blocks.d.ts +99 -0
- package/dist/page/actionresult-blocks.js +144 -0
- package/dist/page/actionresult-shape.d.ts +48 -0
- package/dist/page/actionresult-shape.js +155 -0
- package/dist/page/actionresult-types.d.ts +368 -0
- package/dist/page/actionresult-types.js +4 -0
- package/dist/page/actionresult.d.ts +4 -0
- package/dist/page/actionresult.js +299 -0
- package/dist/page/actions-probe.d.ts +32 -0
- package/dist/page/actions-probe.js +294 -0
- package/dist/page/actions-scroll.d.ts +40 -0
- package/dist/page/actions-scroll.js +53 -0
- package/dist/page/actions.d.ts +132 -0
- package/dist/page/actions.js +453 -0
- package/dist/page/archive-assets.d.ts +39 -0
- package/dist/page/archive-assets.js +187 -0
- package/dist/page/archive.d.ts +47 -0
- package/dist/page/archive.js +349 -0
- package/dist/page/asset-export.d.ts +122 -0
- package/dist/page/asset-export.js +376 -0
- package/dist/page/await_network.d.ts +16 -0
- package/dist/page/await_network.js +23 -0
- package/dist/page/bbox.d.ts +37 -0
- package/dist/page/bbox.js +115 -0
- package/dist/page/canvas-capture.d.ts +82 -0
- package/dist/page/canvas-capture.js +257 -0
- package/dist/page/canvas-diff.d.ts +51 -0
- package/dist/page/canvas-diff.js +131 -0
- package/dist/page/canvas-gesture.d.ts +53 -0
- package/dist/page/canvas-gesture.js +167 -0
- package/dist/page/canvas-transform.d.ts +96 -0
- package/dist/page/canvas-transform.js +150 -0
- package/dist/page/canvas.d.ts +8 -0
- package/dist/page/canvas.js +50 -0
- package/dist/page/capture-substrate.d.ts +111 -0
- package/dist/page/capture-substrate.js +139 -0
- package/dist/page/clipboard.d.ts +25 -0
- package/dist/page/clipboard.js +50 -0
- package/dist/page/clock.d.ts +36 -0
- package/dist/page/clock.js +167 -0
- package/dist/page/compose.d.ts +55 -0
- package/dist/page/compose.js +169 -0
- package/dist/page/console.d.ts +39 -0
- package/dist/page/console.js +73 -0
- package/dist/page/coverage.d.ts +97 -0
- package/dist/page/coverage.js +280 -0
- package/dist/page/dom-export.d.ts +41 -0
- package/dist/page/dom-export.js +193 -0
- package/dist/page/dom-walk.d.ts +91 -0
- package/dist/page/dom-walk.js +267 -0
- package/dist/page/dom_diff.d.ts +48 -0
- package/dist/page/dom_diff.js +121 -0
- package/dist/page/downloads.d.ts +80 -0
- package/dist/page/downloads.js +244 -0
- package/dist/page/drop-files.d.ts +78 -0
- package/dist/page/drop-files.js +310 -0
- package/dist/page/element-export-discovery.d.ts +64 -0
- package/dist/page/element-export-discovery.js +346 -0
- package/dist/page/element-export.d.ts +46 -0
- package/dist/page/element-export.js +251 -0
- package/dist/page/emulation-substrate.d.ts +53 -0
- package/dist/page/emulation-substrate.js +87 -0
- package/dist/page/emulation.d.ts +60 -0
- package/dist/page/emulation.js +162 -0
- package/dist/page/export-playwright-script.d.ts +47 -0
- package/dist/page/export-playwright-script.js +304 -0
- package/dist/page/extract-resolve.d.ts +22 -0
- package/dist/page/extract-resolve.js +341 -0
- package/dist/page/extract-schema.d.ts +20 -0
- package/dist/page/extract-schema.js +200 -0
- package/dist/page/extract-types.d.ts +127 -0
- package/dist/page/extract-types.js +8 -0
- package/dist/page/extract-warnings.d.ts +8 -0
- package/dist/page/extract-warnings.js +56 -0
- package/dist/page/extract.d.ts +9 -0
- package/dist/page/extract.js +174 -0
- package/dist/page/fill-form.d.ts +58 -0
- package/dist/page/fill-form.js +261 -0
- package/dist/page/find.d.ts +158 -0
- package/dist/page/find.js +470 -0
- package/dist/page/frames.d.ts +45 -0
- package/dist/page/frames.js +133 -0
- package/dist/page/generate-locator.d.ts +57 -0
- package/dist/page/generate-locator.js +136 -0
- package/dist/page/gestures.d.ts +128 -0
- package/dist/page/gestures.js +198 -0
- package/dist/page/har.d.ts +91 -0
- package/dist/page/har.js +174 -0
- package/dist/page/heap.d.ts +97 -0
- package/dist/page/heap.js +285 -0
- package/dist/page/inspect.d.ts +34 -0
- package/dist/page/inspect.js +75 -0
- package/dist/page/layout-thrash.d.ts +34 -0
- package/dist/page/layout-thrash.js +232 -0
- package/dist/page/learning.d.ts +21 -0
- package/dist/page/learning.js +84 -0
- package/dist/page/locator.d.ts +54 -0
- package/dist/page/locator.js +142 -0
- package/dist/page/memory-diff.d.ts +48 -0
- package/dist/page/memory-diff.js +105 -0
- package/dist/page/network-mask.d.ts +8 -0
- package/dist/page/network-mask.js +18 -0
- package/dist/page/network-playwright.d.ts +96 -0
- package/dist/page/network-playwright.js +353 -0
- package/dist/page/network-substrate-select.d.ts +18 -0
- package/dist/page/network-substrate-select.js +32 -0
- package/dist/page/network-substrate.d.ts +109 -0
- package/dist/page/network-substrate.js +161 -0
- package/dist/page/network-ws.d.ts +46 -0
- package/dist/page/network-ws.js +113 -0
- package/dist/page/network.d.ts +194 -0
- package/dist/page/network.js +415 -0
- package/dist/page/overflow-detect.d.ts +102 -0
- package/dist/page/overflow-detect.js +449 -0
- package/dist/page/pdf.d.ts +69 -0
- package/dist/page/pdf.js +109 -0
- package/dist/page/perf-audit-analysers.d.ts +40 -0
- package/dist/page/perf-audit-analysers.js +369 -0
- package/dist/page/perf-audit-runner.d.ts +20 -0
- package/dist/page/perf-audit-runner.js +195 -0
- package/dist/page/perf-audit-types.d.ts +41 -0
- package/dist/page/perf-audit-types.js +5 -0
- package/dist/page/perf-audit.d.ts +37 -0
- package/dist/page/perf-audit.js +377 -0
- package/dist/page/perf.d.ts +127 -0
- package/dist/page/perf.js +373 -0
- package/dist/page/plan.d.ts +192 -0
- package/dist/page/plan.js +308 -0
- package/dist/page/point_probe.d.ts +46 -0
- package/dist/page/point_probe.js +99 -0
- package/dist/page/recording.d.ts +67 -0
- package/dist/page/recording.js +172 -0
- package/dist/page/refs.d.ts +92 -0
- package/dist/page/refs.js +134 -0
- package/dist/page/regions.d.ts +23 -0
- package/dist/page/regions.js +32 -0
- package/dist/page/routes.d.ts +40 -0
- package/dist/page/routes.js +87 -0
- package/dist/page/safari-actions.d.ts +12 -0
- package/dist/page/safari-actions.js +144 -0
- package/dist/page/sample.d.ts +64 -0
- package/dist/page/sample.js +216 -0
- package/dist/page/screenshot-on.d.ts +51 -0
- package/dist/page/screenshot-on.js +150 -0
- package/dist/page/screenshot-save.d.ts +36 -0
- package/dist/page/screenshot-save.js +53 -0
- package/dist/page/screenshot-schedule.d.ts +50 -0
- package/dist/page/screenshot-schedule.js +155 -0
- package/dist/page/script-substrate.d.ts +32 -0
- package/dist/page/script-substrate.js +47 -0
- package/dist/page/seed-random.d.ts +45 -0
- package/dist/page/seed-random.js +144 -0
- package/dist/page/set-of-marks.d.ts +96 -0
- package/dist/page/set-of-marks.js +245 -0
- package/dist/page/shadow.d.ts +136 -0
- package/dist/page/shadow.js +400 -0
- package/dist/page/shortcut.d.ts +50 -0
- package/dist/page/shortcut.js +147 -0
- package/dist/page/snapshot-substrate-safari.d.ts +30 -0
- package/dist/page/snapshot-substrate-safari.js +84 -0
- package/dist/page/snapshot-substrate-select.d.ts +24 -0
- package/dist/page/snapshot-substrate-select.js +34 -0
- package/dist/page/snapshot-substrate.d.ts +58 -0
- package/dist/page/snapshot-substrate.js +135 -0
- package/dist/page/snapshot.d.ts +24 -0
- package/dist/page/snapshot.js +162 -0
- package/dist/page/solve-captcha.d.ts +76 -0
- package/dist/page/solve-captcha.js +286 -0
- package/dist/page/storage-substrate-types.d.ts +221 -0
- package/dist/page/storage-substrate-types.js +6 -0
- package/dist/page/storage-substrate.d.ts +215 -0
- package/dist/page/storage-substrate.js +280 -0
- package/dist/page/structural.d.ts +9 -0
- package/dist/page/structural.js +152 -0
- package/dist/page/substrate-bundle-safari.d.ts +8 -0
- package/dist/page/substrate-bundle-safari.js +42 -0
- package/dist/page/substrate-bundle.d.ts +6 -0
- package/dist/page/substrate-bundle.js +53 -0
- package/dist/page/text_search.d.ts +44 -0
- package/dist/page/text_search.js +90 -0
- package/dist/page/upload.d.ts +28 -0
- package/dist/page/upload.js +62 -0
- package/dist/page/verify.d.ts +63 -0
- package/dist/page/verify.js +451 -0
- package/dist/page/video.d.ts +115 -0
- package/dist/page/video.js +169 -0
- package/dist/page/visibility.d.ts +22 -0
- package/dist/page/visibility.js +94 -0
- package/dist/page/watch.d.ts +29 -0
- package/dist/page/watch.js +99 -0
- package/dist/page/workers.d.ts +126 -0
- package/dist/page/workers.js +490 -0
- package/dist/page/ws-interactive.d.ts +82 -0
- package/dist/page/ws-interactive.js +318 -0
- package/dist/plugin/cli.d.ts +45 -0
- package/dist/plugin/cli.js +496 -0
- package/dist/plugin/command-registry.d.ts +9 -0
- package/dist/plugin/command-registry.js +23 -0
- package/dist/plugin/depgraph.d.ts +37 -0
- package/dist/plugin/depgraph.js +186 -0
- package/dist/plugin/manifest.d.ts +182 -0
- package/dist/plugin/manifest.js +219 -0
- package/dist/plugin/package-manager.d.ts +22 -0
- package/dist/plugin/package-manager.js +40 -0
- package/dist/plugin/resolver.d.ts +85 -0
- package/dist/plugin/resolver.js +166 -0
- package/dist/plugin/runtime.d.ts +77 -0
- package/dist/plugin/runtime.js +402 -0
- package/dist/plugin/types.d.ts +113 -0
- package/dist/plugin/types.js +4 -0
- package/dist/policy/confirm.d.ts +76 -0
- package/dist/policy/confirm.js +162 -0
- package/dist/policy/origin.d.ts +17 -0
- package/dist/policy/origin.js +79 -0
- package/dist/sdk/client.d.ts +21 -0
- package/dist/sdk/client.js +174 -0
- package/dist/sdk/index.d.ts +32 -0
- package/dist/sdk/index.js +61 -0
- package/dist/sdk/plugin-types.d.ts +33 -0
- package/dist/sdk/plugin-types.js +22 -0
- package/dist/sdk/registry.d.ts +17 -0
- package/dist/sdk/registry.js +94 -0
- package/dist/sdk/socket-transport.d.ts +20 -0
- package/dist/sdk/socket-transport.js +90 -0
- package/dist/sdk/tool-types.d.ts +634 -0
- package/dist/sdk/tool-types.js +28 -0
- package/dist/sdk/transport-in-process.d.ts +21 -0
- package/dist/sdk/transport-in-process.js +44 -0
- package/dist/sdk/transport-registry.d.ts +19 -0
- package/dist/sdk/transport-registry.js +31 -0
- package/dist/sdk/transport-socket.d.ts +12 -0
- package/dist/sdk/transport-socket.js +77 -0
- package/dist/sdk/transport-stdio-child.d.ts +10 -0
- package/dist/sdk/transport-stdio-child.js +47 -0
- package/dist/sdk/transport.d.ts +10 -0
- package/dist/sdk/transport.js +35 -0
- package/dist/sdk/types.d.ts +176 -0
- package/dist/sdk/types.js +10 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.js +327 -0
- package/dist/session/artifacts.d.ts +52 -0
- package/dist/session/artifacts.js +177 -0
- package/dist/session/byob-attach.d.ts +26 -0
- package/dist/session/byob-attach.js +187 -0
- package/dist/session/byob.d.ts +8 -0
- package/dist/session/byob.js +20 -0
- package/dist/session/cache-storage.d.ts +100 -0
- package/dist/session/cache-storage.js +166 -0
- package/dist/session/device-emu.d.ts +149 -0
- package/dist/session/device-emu.js +545 -0
- package/dist/session/device.d.ts +14 -0
- package/dist/session/device.js +44 -0
- package/dist/session/dialog.d.ts +62 -0
- package/dist/session/dialog.js +164 -0
- package/dist/session/emulation.d.ts +69 -0
- package/dist/session/emulation.js +168 -0
- package/dist/session/extensions.d.ts +113 -0
- package/dist/session/extensions.js +237 -0
- package/dist/session/fs-picker.d.ts +144 -0
- package/dist/session/fs-picker.js +666 -0
- package/dist/session/idb-storage.d.ts +86 -0
- package/dist/session/idb-storage.js +229 -0
- package/dist/session/incognito.d.ts +3 -0
- package/dist/session/incognito.js +20 -0
- package/dist/session/launch-options.d.ts +41 -0
- package/dist/session/launch-options.js +200 -0
- package/dist/session/managed.d.ts +3 -0
- package/dist/session/managed.js +16 -0
- package/dist/session/metrics.d.ts +45 -0
- package/dist/session/metrics.js +75 -0
- package/dist/session/notification.d.ts +122 -0
- package/dist/session/notification.js +426 -0
- package/dist/session/permission.d.ts +144 -0
- package/dist/session/permission.js +600 -0
- package/dist/session/playwright-post-wire.d.ts +8 -0
- package/dist/session/playwright-post-wire.js +148 -0
- package/dist/session/policy-buffer.d.ts +21 -0
- package/dist/session/policy-buffer.js +47 -0
- package/dist/session/profile-snapshot.d.ts +11 -0
- package/dist/session/profile-snapshot.js +53 -0
- package/dist/session/registry.d.ts +365 -0
- package/dist/session/registry.js +98 -0
- package/dist/session/safari-post-wire.d.ts +8 -0
- package/dist/session/safari-post-wire.js +28 -0
- package/dist/session/safari-session.d.ts +10 -0
- package/dist/session/safari-session.js +39 -0
- package/dist/session/storage.d.ts +148 -0
- package/dist/session/storage.js +350 -0
- package/dist/session/types.d.ts +113 -0
- package/dist/session/types.js +5 -0
- package/dist/session/wedge.d.ts +15 -0
- package/dist/session/wedge.js +41 -0
- package/dist/tools/action-core-tools.d.ts +13 -0
- package/dist/tools/action-core-tools.js +156 -0
- package/dist/tools/action-form-tools.d.ts +12 -0
- package/dist/tools/action-form-tools.js +179 -0
- package/dist/tools/action-gesture-tools.d.ts +9 -0
- package/dist/tools/action-gesture-tools.js +115 -0
- package/dist/tools/action-history-tools.d.ts +8 -0
- package/dist/tools/action-history-tools.js +67 -0
- package/dist/tools/action-tool.d.ts +42 -0
- package/dist/tools/action-tool.js +58 -0
- package/dist/tools/action-tools.d.ts +20 -0
- package/dist/tools/action-tools.js +28 -0
- package/dist/tools/batch-act-tools.d.ts +10 -0
- package/dist/tools/batch-act-tools.js +276 -0
- package/dist/tools/batch-human-tools.d.ts +8 -0
- package/dist/tools/batch-human-tools.js +148 -0
- package/dist/tools/canvas-tools.d.ts +40 -0
- package/dist/tools/canvas-tools.js +368 -0
- package/dist/tools/capture-report-diagnostics-tools.d.ts +7 -0
- package/dist/tools/capture-report-diagnostics-tools.js +318 -0
- package/dist/tools/capture-report-element-export-tools.d.ts +8 -0
- package/dist/tools/capture-report-element-export-tools.js +197 -0
- package/dist/tools/capture-report-export-tools.d.ts +8 -0
- package/dist/tools/capture-report-export-tools.js +246 -0
- package/dist/tools/capture-report-marks-tools.d.ts +9 -0
- package/dist/tools/capture-report-marks-tools.js +221 -0
- package/dist/tools/capture-report-upload-tools.d.ts +8 -0
- package/dist/tools/capture-report-upload-tools.js +277 -0
- package/dist/tools/config-approval-tools.d.ts +8 -0
- package/dist/tools/config-approval-tools.js +166 -0
- package/dist/tools/deep-coverage-tools.d.ts +8 -0
- package/dist/tools/deep-coverage-tools.js +325 -0
- package/dist/tools/deep-determinism-tools.d.ts +8 -0
- package/dist/tools/deep-determinism-tools.js +276 -0
- package/dist/tools/deep-perf-tools.d.ts +19 -0
- package/dist/tools/deep-perf-tools.js +324 -0
- package/dist/tools/device-emulation-tools.d.ts +9 -0
- package/dist/tools/device-emulation-tools.js +137 -0
- package/dist/tools/extensions-batch-tools.d.ts +18 -0
- package/dist/tools/extensions-batch-tools.js +24 -0
- package/dist/tools/extensions-rebuild.d.ts +22 -0
- package/dist/tools/extensions-rebuild.js +208 -0
- package/dist/tools/extensions-tools.d.ts +2 -0
- package/dist/tools/extensions-tools.js +331 -0
- package/dist/tools/forms-fill-tools.d.ts +8 -0
- package/dist/tools/forms-fill-tools.js +109 -0
- package/dist/tools/forms-plan-tools.d.ts +7 -0
- package/dist/tools/forms-plan-tools.js +159 -0
- package/dist/tools/forms-recording-mode-tools.d.ts +8 -0
- package/dist/tools/forms-recording-mode-tools.js +71 -0
- package/dist/tools/forms-recording-tools.d.ts +14 -0
- package/dist/tools/forms-recording-tools.js +22 -0
- package/dist/tools/forms-refs-tools.d.ts +8 -0
- package/dist/tools/forms-refs-tools.js +90 -0
- package/dist/tools/gesture-coord-tools.d.ts +8 -0
- package/dist/tools/gesture-coord-tools.js +168 -0
- package/dist/tools/gesture-emulation-tools.d.ts +8 -0
- package/dist/tools/gesture-emulation-tools.js +135 -0
- package/dist/tools/gesture-network-tools.d.ts +17 -0
- package/dist/tools/gesture-network-tools.js +27 -0
- package/dist/tools/gesture-route-tools.d.ts +8 -0
- package/dist/tools/gesture-route-tools.js +142 -0
- package/dist/tools/gesture-websocket-tools.d.ts +8 -0
- package/dist/tools/gesture-websocket-tools.js +122 -0
- package/dist/tools/gesture-worker-tools.d.ts +9 -0
- package/dist/tools/gesture-worker-tools.js +200 -0
- package/dist/tools/host-build.d.ts +76 -0
- package/dist/tools/host-build.js +516 -0
- package/dist/tools/host.d.ts +287 -0
- package/dist/tools/host.js +1 -0
- package/dist/tools/input-tools.d.ts +10 -0
- package/dist/tools/input-tools.js +176 -0
- package/dist/tools/live-emulation-tools.d.ts +9 -0
- package/dist/tools/live-emulation-tools.js +353 -0
- package/dist/tools/plugin-runtime.d.ts +36 -0
- package/dist/tools/plugin-runtime.js +274 -0
- package/dist/tools/read-observe-buffer-tools.d.ts +9 -0
- package/dist/tools/read-observe-buffer-tools.js +385 -0
- package/dist/tools/read-observe-capture-tools.d.ts +12 -0
- package/dist/tools/read-observe-capture-tools.js +376 -0
- package/dist/tools/read-observe-dom-tools.d.ts +8 -0
- package/dist/tools/read-observe-dom-tools.js +308 -0
- package/dist/tools/read-observe-extract-tools.d.ts +8 -0
- package/dist/tools/read-observe-extract-tools.js +232 -0
- package/dist/tools/read-observe-verify-tools.d.ts +8 -0
- package/dist/tools/read-observe-verify-tools.js +316 -0
- package/dist/tools/schemas.d.ts +29 -0
- package/dist/tools/schemas.js +58 -0
- package/dist/tools/secrets-captcha-tools.d.ts +9 -0
- package/dist/tools/secrets-captcha-tools.js +231 -0
- package/dist/tools/session-dialog-permission-tools.d.ts +9 -0
- package/dist/tools/session-dialog-permission-tools.js +287 -0
- package/dist/tools/session-lifecycle-tools.d.ts +8 -0
- package/dist/tools/session-lifecycle-tools.js +314 -0
- package/dist/tools/session-notification-device-tools.d.ts +9 -0
- package/dist/tools/session-notification-device-tools.js +156 -0
- package/dist/tools/session-policy-tools.d.ts +16 -0
- package/dist/tools/session-policy-tools.js +22 -0
- package/dist/tools/session-registry.d.ts +28 -0
- package/dist/tools/session-registry.js +427 -0
- package/dist/tools/storage-artifact-har-video-tools.d.ts +8 -0
- package/dist/tools/storage-artifact-har-video-tools.js +311 -0
- package/dist/tools/storage-cache-idb-tools.d.ts +8 -0
- package/dist/tools/storage-cache-idb-tools.js +347 -0
- package/dist/tools/storage-state-cookies-tools.d.ts +8 -0
- package/dist/tools/storage-state-cookies-tools.js +223 -0
- package/dist/tools/storage-tools.d.ts +17 -0
- package/dist/tools/storage-tools.js +25 -0
- package/dist/tools/storage-web-auth-tools.d.ts +10 -0
- package/dist/tools/storage-web-auth-tools.js +230 -0
- package/dist/tools/tool-metadata.d.ts +8 -0
- package/dist/tools/tool-metadata.js +185 -0
- package/dist/util/batch.d.ts +83 -0
- package/dist/util/batch.js +191 -0
- package/dist/util/capabilities.d.ts +504 -0
- package/dist/util/capabilities.js +254 -0
- package/dist/util/config-store.d.ts +103 -0
- package/dist/util/config-store.js +206 -0
- package/dist/util/config.d.ts +11 -0
- package/dist/util/config.js +28 -0
- package/dist/util/credentials.d.ts +136 -0
- package/dist/util/credentials.js +622 -0
- package/dist/util/deadline.d.ts +22 -0
- package/dist/util/deadline.js +62 -0
- package/dist/util/diagnostics.d.ts +161 -0
- package/dist/util/diagnostics.js +579 -0
- package/dist/util/egress-sanitiser.d.ts +29 -0
- package/dist/util/egress-sanitiser.js +52 -0
- package/dist/util/failure.d.ts +8 -0
- package/dist/util/failure.js +50 -0
- package/dist/util/flake-check.d.ts +109 -0
- package/dist/util/flake-check.js +342 -0
- package/dist/util/invariant.d.ts +25 -0
- package/dist/util/invariant.js +66 -0
- package/dist/util/logging.d.ts +6 -0
- package/dist/util/logging.js +12 -0
- package/dist/util/predicates.d.ts +62 -0
- package/dist/util/predicates.js +340 -0
- package/dist/util/secrets.d.ts +104 -0
- package/dist/util/secrets.js +219 -0
- package/dist/util/tokens.d.ts +6 -0
- package/dist/util/tokens.js +24 -0
- package/dist/util/url-sanitizer.d.ts +19 -0
- package/dist/util/url-sanitizer.js +70 -0
- package/dist/util/version.d.ts +2 -0
- package/dist/util/version.js +21 -0
- package/dist/util/workspace.d.ts +7 -0
- package/dist/util/workspace.js +22 -0
- package/package.json +120 -0
package/dist/page/har.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// HAR (HTTP Archive) record/replay primitives. Full-session reproducibility:
|
|
2
|
+
// capture every request the page made into a HAR file, then later replay a
|
|
3
|
+
// session with `open_session({hars:[file]})` so navigation/XHR/fetch are served
|
|
4
|
+
// from the archive instead of hitting the network.
|
|
5
|
+
//
|
|
6
|
+
// Recording strategy. Playwright's HAR is finalized on `context.close()` — there
|
|
7
|
+
// is no public mid-session flush. We honour that:
|
|
8
|
+
//
|
|
9
|
+
// - `start_har({path?})` calls `context.routeFromHAR(harPath, {update:true,
|
|
10
|
+
// updateMode:"full", updateContent:"embed"})`. From that point every request
|
|
11
|
+
// in the context is logged into the in-memory HAR.
|
|
12
|
+
// - `stop_har()` calls `context.unrouteAll({behavior:"wait"})` to remove the
|
|
13
|
+
// recording route. The HAR FILE on disk is written when the context closes
|
|
14
|
+
// (`close_session` is the standard finalize point). Until then the path is
|
|
15
|
+
// reserved + tracked; the caller's flow is "start_har → drive → stop_har →
|
|
16
|
+
// close_session → read the .har". This Playwright constraint is documented
|
|
17
|
+
// on every return shape.
|
|
18
|
+
// - For up-front recording across the whole session, prefer the additive
|
|
19
|
+
// `open_session({har: {...}})` schema — that routes through Playwright's
|
|
20
|
+
// `recordHar` context option, which is also finalized on close but is the
|
|
21
|
+
// blessed native primitive.
|
|
22
|
+
//
|
|
23
|
+
// Replay strategy. `open_session({hars: ["file.har", ...]})` calls
|
|
24
|
+
// `context.routeFromHAR(file, {notFound:"fallback"})` on each path immediately
|
|
25
|
+
// after context creation. Files that escape the workspace are rejected (the
|
|
26
|
+
// `resolveWorkspacePath` helper); a missing file is a hard error (no silent
|
|
27
|
+
// fallback to network on a typo).
|
|
28
|
+
//
|
|
29
|
+
// Per-session state lives in `SessionEntry.har`; the registry's HAR field is
|
|
30
|
+
// initialised at session creation and consulted by `start_har`/`stop_har`.
|
|
31
|
+
import { existsSync, mkdirSync, statSync, readFileSync } from "node:fs";
|
|
32
|
+
import { dirname } from "node:path";
|
|
33
|
+
import { resolveWorkspacePath } from "../session/storage.js";
|
|
34
|
+
/** Maximum size (in bytes) at which a finalized HAR is returned inline rather
|
|
35
|
+
* than only by path. Mirrors the same cap used by `network_body` / storage
|
|
36
|
+
* dumps — agents that hit it should rely on the path field instead. */
|
|
37
|
+
export const HAR_INLINE_CAP_BYTES = 256 * 1024;
|
|
38
|
+
export function newHarRecorderState() {
|
|
39
|
+
return { active: false };
|
|
40
|
+
}
|
|
41
|
+
/** Default HAR filename for an auto-named recording. ISO timestamp with
|
|
42
|
+
* `:` / `.` mapped to `-` so the name is filesystem-safe on every platform. */
|
|
43
|
+
export function defaultHarFilename(sessionId, now = new Date()) {
|
|
44
|
+
const iso = now.toISOString().replace(/[:.]/g, "-");
|
|
45
|
+
const safeId = sessionId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
46
|
+
return `${safeId}-${iso}.har`;
|
|
47
|
+
}
|
|
48
|
+
/** Resolve an explicit user-supplied path (workspace-escape rejected) OR
|
|
49
|
+
* build the default `<workspace>/har/<auto>.har` path. Creates the parent
|
|
50
|
+
* dir on demand — still under the workspace root by construction. */
|
|
51
|
+
export function resolveHarPath(workspaceRoot, sessionId, userPath, tool) {
|
|
52
|
+
const resolved = userPath
|
|
53
|
+
? resolveWorkspacePath(workspaceRoot, userPath, tool)
|
|
54
|
+
: resolveWorkspacePath(workspaceRoot, `har/${defaultHarFilename(sessionId)}`, tool);
|
|
55
|
+
// `resolved` is workspace-rooted by construction (resolveWorkspacePath rejects
|
|
56
|
+
// any escape from `workspaceRoot`); so `dirname(resolved)` is workspace-rooted
|
|
57
|
+
// too — the mkdirSync below never touches cwd. BROWX_WORKSPACE-derived.
|
|
58
|
+
const parent = dirname(resolved);
|
|
59
|
+
if (parent && parent !== resolved && !existsSync(parent)) {
|
|
60
|
+
mkdirSync(parent, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
/** Validate replay HAR file paths supplied to `open_session({hars})`. Each
|
|
65
|
+
* must resolve under `$BROWX_WORKSPACE` and the file must exist (a typo
|
|
66
|
+
* silently falling back to live network would defeat the point). */
|
|
67
|
+
export function resolveHarReplayPaths(workspaceRoot, hars, tool) {
|
|
68
|
+
const out = [];
|
|
69
|
+
for (const h of hars) {
|
|
70
|
+
if (typeof h !== "string" || !h) {
|
|
71
|
+
throw new Error(`${tool}: \`hars\` entries must be non-empty workspace-rooted strings`);
|
|
72
|
+
}
|
|
73
|
+
const resolved = resolveWorkspacePath(workspaceRoot, h, tool);
|
|
74
|
+
if (!existsSync(resolved)) {
|
|
75
|
+
throw new Error(`${tool}: HAR replay file not found at "${resolved}"`);
|
|
76
|
+
}
|
|
77
|
+
out.push(resolved);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/** Begin HAR recording on a live context via `routeFromHAR(update:true)`. The
|
|
82
|
+
* HAR file on disk is finalized when the context closes — this returns the
|
|
83
|
+
* reserved path and the caller is responsible for `close_session` (or the
|
|
84
|
+
* natural session teardown) to flush. Re-calling `start_har` on an already-
|
|
85
|
+
* active recorder replaces the in-flight target after a transparent stop. */
|
|
86
|
+
export async function startHar(context, state, workspaceRoot, sessionId, cfg = {}) {
|
|
87
|
+
if (state.nativeRecord) {
|
|
88
|
+
throw new Error("start_har: HAR recording was already wired at session creation via " +
|
|
89
|
+
"`open_session({har})`. Close the session and re-open without the `har` " +
|
|
90
|
+
"field if you need start/stop granularity.");
|
|
91
|
+
}
|
|
92
|
+
const replacedPrior = state.active;
|
|
93
|
+
if (replacedPrior) {
|
|
94
|
+
// Best-effort stop of the prior recorder before swapping targets. Without
|
|
95
|
+
// this a second `routeFromHAR(update:true)` chains rather than replacing.
|
|
96
|
+
await context.unrouteAll({ behavior: "wait" }).catch(() => undefined);
|
|
97
|
+
}
|
|
98
|
+
const path = resolveHarPath(workspaceRoot, sessionId, cfg.path, "start_har");
|
|
99
|
+
const mode = cfg.mode ?? "full";
|
|
100
|
+
const content = cfg.content ?? "embed";
|
|
101
|
+
const options = {
|
|
102
|
+
update: true,
|
|
103
|
+
updateMode: mode,
|
|
104
|
+
updateContent: content === "omit" ? undefined : content,
|
|
105
|
+
};
|
|
106
|
+
if (cfg.urlFilter !== undefined)
|
|
107
|
+
options.url = cfg.urlFilter;
|
|
108
|
+
await context.routeFromHAR(path, options);
|
|
109
|
+
state.active = true;
|
|
110
|
+
state.path = path;
|
|
111
|
+
state.startedAt = Date.now();
|
|
112
|
+
state.mode = mode;
|
|
113
|
+
state.content = content;
|
|
114
|
+
state.nativeRecord = false;
|
|
115
|
+
return { path, mode, content, replacedPrior };
|
|
116
|
+
}
|
|
117
|
+
/** Stop HAR recording on a live context. Removes the recording route; the HAR
|
|
118
|
+
* file is written to disk when the context closes (Playwright constraint).
|
|
119
|
+
* No-op + `{wasActive:false}` when no recorder is active. */
|
|
120
|
+
export async function stopHar(context, state) {
|
|
121
|
+
if (!state.active) {
|
|
122
|
+
return { wasActive: false, finalized: false, nativeRecord: !!state.nativeRecord };
|
|
123
|
+
}
|
|
124
|
+
if (state.nativeRecord) {
|
|
125
|
+
// Recording was wired at context creation — Playwright doesn't expose a
|
|
126
|
+
// mid-session disable for that path. Surface the constraint instead of
|
|
127
|
+
// silently lying about having stopped.
|
|
128
|
+
return { wasActive: true, path: state.path, finalized: false, nativeRecord: true };
|
|
129
|
+
}
|
|
130
|
+
await context.unrouteAll({ behavior: "wait" }).catch(() => undefined);
|
|
131
|
+
const path = state.path;
|
|
132
|
+
state.active = false;
|
|
133
|
+
// Keep `path` discoverable on the state until session close so callers can
|
|
134
|
+
// still find the file after teardown.
|
|
135
|
+
return { wasActive: true, path, finalized: false, nativeRecord: false };
|
|
136
|
+
}
|
|
137
|
+
/** Best-effort read of a finalized HAR. Used by callers that want the file
|
|
138
|
+
* inlined when small. Returns `undefined` when the file doesn't yet exist
|
|
139
|
+
* (HAR not finalized) or is over the inline cap. */
|
|
140
|
+
export function readHarIfSmall(path, capBytes = HAR_INLINE_CAP_BYTES) {
|
|
141
|
+
if (!existsSync(path))
|
|
142
|
+
return undefined;
|
|
143
|
+
const st = statSync(path);
|
|
144
|
+
if (st.size > capBytes)
|
|
145
|
+
return undefined;
|
|
146
|
+
return readFileSync(path, "utf8");
|
|
147
|
+
}
|
|
148
|
+
/** Apply replay HAR(s) to a live context. Each file is wired with
|
|
149
|
+
* `notFound:"fallback"` so a request that isn't in the archive falls through
|
|
150
|
+
* to the live network (the safer default; agents who want a hermetic replay
|
|
151
|
+
* can route the rest themselves). */
|
|
152
|
+
export async function applyHarReplay(context, files) {
|
|
153
|
+
for (const f of files) {
|
|
154
|
+
await context.routeFromHAR(f, { notFound: "fallback" });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Build the Playwright `recordHar` option for `open_session({har})`. The
|
|
158
|
+
* caller passes this into `browser.newContext({recordHar})` / equivalent
|
|
159
|
+
* on `chromium.launchPersistentContext`. */
|
|
160
|
+
export function buildRecordHarOption(workspaceRoot, sessionId, cfg) {
|
|
161
|
+
const path = resolveHarPath(workspaceRoot, sessionId, cfg.path, "open_session");
|
|
162
|
+
const mode = cfg.mode ?? "full";
|
|
163
|
+
const content = cfg.content ?? "embed";
|
|
164
|
+
const recordHar = {
|
|
165
|
+
path,
|
|
166
|
+
mode,
|
|
167
|
+
content,
|
|
168
|
+
};
|
|
169
|
+
if (cfg.urlFilter !== undefined)
|
|
170
|
+
recordHar.urlFilter = cfg.urlFilter;
|
|
171
|
+
// Parent dir is ensured by `resolveHarPath`. The path is workspace-rooted by
|
|
172
|
+
// construction — `resolveWorkspacePath` rejects escape.
|
|
173
|
+
return { path, mode, content, recordHar };
|
|
174
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { CDPSession } from "playwright-core";
|
|
2
|
+
/** Take a V8 heap snapshot on `cdp`. Returns the full snapshot JSON as a
|
|
3
|
+
* string (the format chrome://inspect's Memory tool consumes). Buffers
|
|
4
|
+
* `HeapProfiler.addHeapSnapshotChunk` events fired during
|
|
5
|
+
* `HeapProfiler.takeHeapSnapshot`. Detaches its listeners on return —
|
|
6
|
+
* no leak across calls. */
|
|
7
|
+
export declare function takeHeapSnapshot(cdp: CDPSession): Promise<string>;
|
|
8
|
+
export declare function resolveHeapSnapshotPath(workspaceRoot: string, p: string, tool: string): string;
|
|
9
|
+
/** Default snapshot filename under
|
|
10
|
+
* `<workspace>/heap-snapshots/<sessionId>-<ts>.heapsnapshot`. The
|
|
11
|
+
* `.heapsnapshot` extension is the one DevTools' Memory panel and
|
|
12
|
+
* `chrome://inspect` recognise on drag-and-drop. */
|
|
13
|
+
export declare function defaultHeapSnapshotPath(workspaceRoot: string, sessionId: string): string;
|
|
14
|
+
/** Write a snapshot JSON string to a workspace-rooted file. Creates the
|
|
15
|
+
* parent dir if missing. Returns the resolved path + byte count. */
|
|
16
|
+
export declare function writeHeapSnapshotFile(workspaceRoot: string, filePath: string, snapshotJson: string, tool: string): {
|
|
17
|
+
resolved: string;
|
|
18
|
+
bytes: number;
|
|
19
|
+
};
|
|
20
|
+
/** Parsed snapshot summary surfaced on `heap_retainers` results. */
|
|
21
|
+
export interface HeapSnapshotSummary {
|
|
22
|
+
nodeCount: number;
|
|
23
|
+
edgeCount: number;
|
|
24
|
+
stringCount: number;
|
|
25
|
+
/** Sum of `self_size` across every node — total heap occupied. */
|
|
26
|
+
totalSelfSize: number;
|
|
27
|
+
}
|
|
28
|
+
/** One retainer row in a `heap_retainers` result. */
|
|
29
|
+
export interface HeapRetainerRow {
|
|
30
|
+
/** Display name of the retainer node: `${type}:${name}` or just `name`. */
|
|
31
|
+
retainerName: string;
|
|
32
|
+
retainerType: string;
|
|
33
|
+
retainerSelfSize: number;
|
|
34
|
+
/** How many distinct edges from this retainer point to a matching node. */
|
|
35
|
+
edgesToMatches: number;
|
|
36
|
+
/** Display names of up to 5 matching nodes this retainer holds. */
|
|
37
|
+
sampleHeldNodes: string[];
|
|
38
|
+
}
|
|
39
|
+
/** Result body of a `heap_retainers` call. */
|
|
40
|
+
export interface HeapRetainersResult {
|
|
41
|
+
summary: HeapSnapshotSummary;
|
|
42
|
+
/** How many nodes the query matched. */
|
|
43
|
+
matchCount: number;
|
|
44
|
+
/** Cap-applied retainer rows, sorted by retainer self_size desc. */
|
|
45
|
+
retainers: HeapRetainerRow[];
|
|
46
|
+
/** Sample of matched node display names (first 10). Lets the caller
|
|
47
|
+
* sanity-check the query without parsing the full retainer set. */
|
|
48
|
+
sampleMatches: string[];
|
|
49
|
+
/** Non-fatal extraction warnings (unknown field layout, etc.). */
|
|
50
|
+
warnings?: string[];
|
|
51
|
+
}
|
|
52
|
+
/** Internal parsed snapshot. */
|
|
53
|
+
interface ParsedSnapshot {
|
|
54
|
+
nodeFields: string[];
|
|
55
|
+
nodeTypes: string[];
|
|
56
|
+
edgeFields: string[];
|
|
57
|
+
edgeTypes: string[];
|
|
58
|
+
nodes: number[];
|
|
59
|
+
edges: number[];
|
|
60
|
+
strings: string[];
|
|
61
|
+
nodeFieldCount: number;
|
|
62
|
+
edgeFieldCount: number;
|
|
63
|
+
/** Index into nodeFields for fast access. */
|
|
64
|
+
fieldIdx: {
|
|
65
|
+
nodeType: number;
|
|
66
|
+
nodeName: number;
|
|
67
|
+
nodeSelfSize: number;
|
|
68
|
+
nodeEdgeCount: number;
|
|
69
|
+
edgeType: number;
|
|
70
|
+
edgeName: number;
|
|
71
|
+
edgeToNode: number;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export declare function parseHeapSnapshot(snapshotJson: string): ParsedSnapshot;
|
|
75
|
+
/** Read a snapshot file from disk + parse. */
|
|
76
|
+
export declare function readHeapSnapshotFile(workspaceRoot: string, filePath: string, tool: string): {
|
|
77
|
+
parsed: ParsedSnapshot;
|
|
78
|
+
resolved: string;
|
|
79
|
+
};
|
|
80
|
+
/** Query input for `heap_retainers`. Either `name` (string match against
|
|
81
|
+
* node display name) or `type` (string match against node type). Both
|
|
82
|
+
* may be set — the row matches when both match. */
|
|
83
|
+
export interface HeapRetainersQuery {
|
|
84
|
+
/** Substring or exact string to match against the node's display name
|
|
85
|
+
* (the V8 string-table entry). Case-sensitive. */
|
|
86
|
+
name?: string;
|
|
87
|
+
/** Exact node-type to match (`"closure"`, `"object"`, `"hidden"`, …).
|
|
88
|
+
* See V8's `node_types[0]` for the catalogue in the snapshot. */
|
|
89
|
+
type?: string;
|
|
90
|
+
/** When `name` is set: match style. `"exact"` (default) requires
|
|
91
|
+
* string equality; `"substring"` allows substring containment. */
|
|
92
|
+
nameMatch?: "exact" | "substring";
|
|
93
|
+
}
|
|
94
|
+
/** Run a retainer query over a parsed snapshot. Pure — exported for unit
|
|
95
|
+
* tests; the public-facing tool composes parse → query. */
|
|
96
|
+
export declare function queryRetainers(p: ParsedSnapshot, q: HeapRetainersQuery): HeapRetainersResult;
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// V8 heap snapshots — capability `action` (writes a file).
|
|
2
|
+
//
|
|
3
|
+
// "This page slowly leaks memory — what's holding the old DOM tree alive?"
|
|
4
|
+
// has no diagnostic surface in browxai's read-only tools: a screenshot /
|
|
5
|
+
// snapshot / network slice shows what's on the page now, not what's still
|
|
6
|
+
// retained from a previous state. CDP `HeapProfiler.takeHeapSnapshot`
|
|
7
|
+
// produces a V8 heap snapshot blob — the same `.heapsnapshot` format
|
|
8
|
+
// chrome://inspect's Memory tab consumes — that lets the agent ask
|
|
9
|
+
// "who points to objects named X / typed Y" against the live VM.
|
|
10
|
+
//
|
|
11
|
+
// Two tools, one lifecycle (no start/stop — a snapshot is a single
|
|
12
|
+
// point-in-time capture, not a recording window):
|
|
13
|
+
// - heap_snapshot({path?}) → write a snapshot file under
|
|
14
|
+
// `<workspace>/heap-snapshots/<id>-<ts>.heapsnapshot`.
|
|
15
|
+
// Default path is in the workspace,
|
|
16
|
+
// explicit `path` is enforced inside
|
|
17
|
+
// the workspace.
|
|
18
|
+
// - heap_retainers({snapshotPath, → parse a written snapshot and return
|
|
19
|
+
// query, …}) top retainers of nodes matching the
|
|
20
|
+
// query (against node name OR class
|
|
21
|
+
// constructor name). Pure file read +
|
|
22
|
+
// in-process parse — no CDP touch.
|
|
23
|
+
//
|
|
24
|
+
// CDP delivers the snapshot as a stream of `HeapProfiler.addHeapSnapshotChunk`
|
|
25
|
+
// events fired during `HeapProfiler.takeHeapSnapshot`. The chunks are JSON
|
|
26
|
+
// fragments that, concatenated in order, form the complete `.heapsnapshot`
|
|
27
|
+
// JSON document. We buffer them and write at end — same pattern as
|
|
28
|
+
// `src/page/perf.ts`'s `Tracing.dataCollected` → `tracingComplete` flow,
|
|
29
|
+
// but without a long-running state-machine (a snapshot is one-shot).
|
|
30
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
31
|
+
import { join, dirname, resolve, sep } from "node:path";
|
|
32
|
+
/** Take a V8 heap snapshot on `cdp`. Returns the full snapshot JSON as a
|
|
33
|
+
* string (the format chrome://inspect's Memory tool consumes). Buffers
|
|
34
|
+
* `HeapProfiler.addHeapSnapshotChunk` events fired during
|
|
35
|
+
* `HeapProfiler.takeHeapSnapshot`. Detaches its listeners on return —
|
|
36
|
+
* no leak across calls. */
|
|
37
|
+
export async function takeHeapSnapshot(cdp) {
|
|
38
|
+
const chunks = [];
|
|
39
|
+
const onChunk = (e) => {
|
|
40
|
+
if (typeof e?.chunk === "string")
|
|
41
|
+
chunks.push(e.chunk);
|
|
42
|
+
};
|
|
43
|
+
cdp.on("HeapProfiler.addHeapSnapshotChunk", onChunk);
|
|
44
|
+
try {
|
|
45
|
+
// `reportProgress:false` — we don't need the progress events, just the
|
|
46
|
+
// chunks. `captureNumericValue` is omitted; the default snapshot covers
|
|
47
|
+
// what a retainer query needs (we don't read primitive values).
|
|
48
|
+
await cdp.send("HeapProfiler.takeHeapSnapshot", { reportProgress: false });
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
cdp.off("HeapProfiler.addHeapSnapshotChunk", onChunk);
|
|
52
|
+
}
|
|
53
|
+
return chunks.join("");
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Workspace path helper — mirrors `resolvePerfTracePath` in src/page/perf.ts.
|
|
57
|
+
export function resolveHeapSnapshotPath(workspaceRoot, p, tool) {
|
|
58
|
+
const resolved = resolve(workspaceRoot, p);
|
|
59
|
+
if (resolved !== workspaceRoot && !resolved.startsWith(workspaceRoot + sep)) {
|
|
60
|
+
throw new Error(`${tool}: \`path\` must resolve inside $BROWX_WORKSPACE — got "${p}".`);
|
|
61
|
+
}
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
/** Default snapshot filename under
|
|
65
|
+
* `<workspace>/heap-snapshots/<sessionId>-<ts>.heapsnapshot`. The
|
|
66
|
+
* `.heapsnapshot` extension is the one DevTools' Memory panel and
|
|
67
|
+
* `chrome://inspect` recognise on drag-and-drop. */
|
|
68
|
+
export function defaultHeapSnapshotPath(workspaceRoot, sessionId) {
|
|
69
|
+
const safe = (sessionId || "default").replace(/[^A-Za-z0-9._-]/g, "_");
|
|
70
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
71
|
+
return join(workspaceRoot, "heap-snapshots", `${safe}-${ts}.heapsnapshot`);
|
|
72
|
+
}
|
|
73
|
+
/** Write a snapshot JSON string to a workspace-rooted file. Creates the
|
|
74
|
+
* parent dir if missing. Returns the resolved path + byte count. */
|
|
75
|
+
export function writeHeapSnapshotFile(workspaceRoot, filePath, snapshotJson, tool) {
|
|
76
|
+
// Path is workspace-rooted by construction via `resolveHeapSnapshotPath`.
|
|
77
|
+
const resolved = resolveHeapSnapshotPath(workspaceRoot, filePath, tool);
|
|
78
|
+
const parent = dirname(resolved);
|
|
79
|
+
// ws.sub-style: ensure parent exists under workspace.root.
|
|
80
|
+
if (parent && !existsSync(parent))
|
|
81
|
+
mkdirSync(parent, { recursive: true });
|
|
82
|
+
// ws.root-rooted path — see resolveHeapSnapshotPath above for the guard.
|
|
83
|
+
writeFileSync(resolved, snapshotJson, "utf8");
|
|
84
|
+
return { resolved, bytes: Buffer.byteLength(snapshotJson, "utf8") };
|
|
85
|
+
}
|
|
86
|
+
const MAX_RETAINER_RESULTS = 50;
|
|
87
|
+
const MAX_SAMPLE_HELD = 5;
|
|
88
|
+
const MAX_SAMPLE_MATCHES = 10;
|
|
89
|
+
/** Parse + validate the heap snapshot JSON into a plain record, throwing a
|
|
90
|
+
* descriptive error on malformed input. */
|
|
91
|
+
function parseSnapshotRoot(snapshotJson) {
|
|
92
|
+
let raw;
|
|
93
|
+
try {
|
|
94
|
+
raw = JSON.parse(snapshotJson);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
throw new Error(`heap snapshot is not valid JSON (${err instanceof Error ? err.message : String(err)})`);
|
|
98
|
+
}
|
|
99
|
+
if (!raw || typeof raw !== "object") {
|
|
100
|
+
throw new Error("heap snapshot: top-level value is not an object");
|
|
101
|
+
}
|
|
102
|
+
return raw;
|
|
103
|
+
}
|
|
104
|
+
/** Extract the field/type name arrays from `snapshot.meta`. */
|
|
105
|
+
function extractSnapshotMeta(r) {
|
|
106
|
+
const snapshot = r.snapshot;
|
|
107
|
+
const meta = snapshot?.meta;
|
|
108
|
+
if (!meta || !Array.isArray(meta.node_fields) || !Array.isArray(meta.edge_fields)) {
|
|
109
|
+
throw new Error("heap snapshot: missing snapshot.meta.node_fields / edge_fields");
|
|
110
|
+
}
|
|
111
|
+
// node_types[0] / edge_types[0] is the array of distinct type names (the others
|
|
112
|
+
// are scalar tags like "string" / "number"). V8 quirks.
|
|
113
|
+
const firstTypeArray = (v) => {
|
|
114
|
+
const arr = Array.isArray(v) ? v : [];
|
|
115
|
+
return Array.isArray(arr[0]) ? arr[0].map(String) : [];
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
nodeFields: meta.node_fields.map(String),
|
|
119
|
+
edgeFields: meta.edge_fields.map(String),
|
|
120
|
+
nodeTypes: firstTypeArray(meta.node_types),
|
|
121
|
+
edgeTypes: firstTypeArray(meta.edge_types),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function parseHeapSnapshot(snapshotJson) {
|
|
125
|
+
const r = parseSnapshotRoot(snapshotJson);
|
|
126
|
+
const { nodeFields, edgeFields, nodeTypes, edgeTypes } = extractSnapshotMeta(r);
|
|
127
|
+
const nodes = Array.isArray(r.nodes) ? r.nodes : null;
|
|
128
|
+
const edges = Array.isArray(r.edges) ? r.edges : null;
|
|
129
|
+
const strings = Array.isArray(r.strings) ? r.strings.map(String) : null;
|
|
130
|
+
if (!nodes || !edges || !strings) {
|
|
131
|
+
throw new Error("heap snapshot: missing nodes / edges / strings arrays");
|
|
132
|
+
}
|
|
133
|
+
const fieldIdx = {
|
|
134
|
+
nodeType: nodeFields.indexOf("type"),
|
|
135
|
+
nodeName: nodeFields.indexOf("name"),
|
|
136
|
+
nodeSelfSize: nodeFields.indexOf("self_size"),
|
|
137
|
+
nodeEdgeCount: nodeFields.indexOf("edge_count"),
|
|
138
|
+
edgeType: edgeFields.indexOf("type"),
|
|
139
|
+
edgeName: edgeFields.indexOf("name_or_index"),
|
|
140
|
+
edgeToNode: edgeFields.indexOf("to_node"),
|
|
141
|
+
};
|
|
142
|
+
// Required fields for retainer walking — bail if any are missing.
|
|
143
|
+
for (const [k, v] of Object.entries(fieldIdx)) {
|
|
144
|
+
if (v < 0) {
|
|
145
|
+
throw new Error(`heap snapshot: required field "${k}" missing from meta`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
nodeFields,
|
|
150
|
+
nodeTypes,
|
|
151
|
+
edgeFields,
|
|
152
|
+
edgeTypes,
|
|
153
|
+
nodes,
|
|
154
|
+
edges,
|
|
155
|
+
strings,
|
|
156
|
+
nodeFieldCount: nodeFields.length,
|
|
157
|
+
edgeFieldCount: edgeFields.length,
|
|
158
|
+
fieldIdx,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/** Read a snapshot file from disk + parse. */
|
|
162
|
+
export function readHeapSnapshotFile(workspaceRoot, filePath, tool) {
|
|
163
|
+
const resolved = resolveHeapSnapshotPath(workspaceRoot, filePath, tool);
|
|
164
|
+
if (!existsSync(resolved)) {
|
|
165
|
+
throw new Error(`${tool}: snapshot file not found at "${resolved}" — call heap_snapshot first`);
|
|
166
|
+
}
|
|
167
|
+
const raw = readFileSync(resolved, "utf8");
|
|
168
|
+
const parsed = parseHeapSnapshot(raw);
|
|
169
|
+
return { parsed, resolved };
|
|
170
|
+
}
|
|
171
|
+
/** Display name for a node — `${type}:${name}` if both useful, else
|
|
172
|
+
* whichever is non-empty. Falls back to `node#${index}`. */
|
|
173
|
+
function nodeDisplayName(p, nodeIdx) {
|
|
174
|
+
const base = nodeIdx * p.nodeFieldCount;
|
|
175
|
+
const typeIdx = p.nodes[base + p.fieldIdx.nodeType] ?? 0;
|
|
176
|
+
const nameIdx = p.nodes[base + p.fieldIdx.nodeName] ?? 0;
|
|
177
|
+
const type = p.nodeTypes[typeIdx] ?? "";
|
|
178
|
+
const name = p.strings[nameIdx] ?? "";
|
|
179
|
+
if (type && name)
|
|
180
|
+
return `${type}:${name}`;
|
|
181
|
+
if (name)
|
|
182
|
+
return name;
|
|
183
|
+
if (type)
|
|
184
|
+
return type;
|
|
185
|
+
return `node#${nodeIdx}`;
|
|
186
|
+
}
|
|
187
|
+
/** Self size of a node. */
|
|
188
|
+
function nodeSelfSize(p, nodeIdx) {
|
|
189
|
+
return p.nodes[nodeIdx * p.nodeFieldCount + p.fieldIdx.nodeSelfSize] ?? 0;
|
|
190
|
+
}
|
|
191
|
+
/** First pass: identify nodes matching the query + accumulate total self-size. */
|
|
192
|
+
function findMatchingNodes(p, q, nodeCount) {
|
|
193
|
+
const nameMatch = q.nameMatch ?? "exact";
|
|
194
|
+
const matchSet = new Set();
|
|
195
|
+
let totalSelfSize = 0;
|
|
196
|
+
for (let i = 0; i < nodeCount; i++) {
|
|
197
|
+
const base = i * p.nodeFieldCount;
|
|
198
|
+
totalSelfSize += p.nodes[base + p.fieldIdx.nodeSelfSize] ?? 0;
|
|
199
|
+
const type = p.nodeTypes[p.nodes[base + p.fieldIdx.nodeType] ?? 0] ?? "";
|
|
200
|
+
if (q.type && type !== q.type)
|
|
201
|
+
continue; // type filter first (cheap)
|
|
202
|
+
if (q.name) {
|
|
203
|
+
const name = p.strings[p.nodes[base + p.fieldIdx.nodeName] ?? 0] ?? "";
|
|
204
|
+
if (nameMatch === "exact" ? name !== q.name : !name.includes(q.name))
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
matchSet.add(i);
|
|
208
|
+
}
|
|
209
|
+
return { matchSet, totalSelfSize };
|
|
210
|
+
}
|
|
211
|
+
/** Second pass: walk every node's edge slice and record retainers of matched
|
|
212
|
+
* nodes. `to_node` is a FIRST-FIELD index into the flat `nodes` array, so divide
|
|
213
|
+
* by `nodeFieldCount` for the logical node index. */
|
|
214
|
+
function walkRetainers(p, matchSet, nodeCount) {
|
|
215
|
+
const retainerMap = new Map();
|
|
216
|
+
let edgeCursor = 0;
|
|
217
|
+
for (let nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) {
|
|
218
|
+
const edgesOnThisNode = p.nodes[nodeIdx * p.nodeFieldCount + p.fieldIdx.nodeEdgeCount] ?? 0;
|
|
219
|
+
for (let e = 0; e < edgesOnThisNode; e++) {
|
|
220
|
+
const edgeBase = (edgeCursor + e) * p.edgeFieldCount;
|
|
221
|
+
const toNodeIdx = ((p.edges[edgeBase + p.fieldIdx.edgeToNode] ?? 0) / p.nodeFieldCount) | 0;
|
|
222
|
+
if (!matchSet.has(toNodeIdx) || nodeIdx === toNodeIdx)
|
|
223
|
+
continue; // skip non-matches + self
|
|
224
|
+
let agg = retainerMap.get(nodeIdx);
|
|
225
|
+
if (!agg) {
|
|
226
|
+
agg = { edgesToMatches: 0, sampleHeldNodes: [] };
|
|
227
|
+
retainerMap.set(nodeIdx, agg);
|
|
228
|
+
}
|
|
229
|
+
agg.edgesToMatches++;
|
|
230
|
+
if (agg.sampleHeldNodes.length < MAX_SAMPLE_HELD) {
|
|
231
|
+
agg.sampleHeldNodes.push(nodeDisplayName(p, toNodeIdx));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
edgeCursor += edgesOnThisNode;
|
|
235
|
+
}
|
|
236
|
+
return retainerMap;
|
|
237
|
+
}
|
|
238
|
+
/** Materialise the retainer rows, sorted by self-size desc then edge count desc,
|
|
239
|
+
* capped at MAX_RETAINER_RESULTS. */
|
|
240
|
+
function materialiseRetainerRows(p, retainerMap) {
|
|
241
|
+
const rows = [];
|
|
242
|
+
for (const [retainerIdx, agg] of retainerMap.entries()) {
|
|
243
|
+
const typeIdx = p.nodes[retainerIdx * p.nodeFieldCount + p.fieldIdx.nodeType] ?? 0;
|
|
244
|
+
rows.push({
|
|
245
|
+
retainerName: nodeDisplayName(p, retainerIdx),
|
|
246
|
+
retainerType: p.nodeTypes[typeIdx] ?? "",
|
|
247
|
+
retainerSelfSize: nodeSelfSize(p, retainerIdx),
|
|
248
|
+
edgesToMatches: agg.edgesToMatches,
|
|
249
|
+
sampleHeldNodes: agg.sampleHeldNodes,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
rows.sort((a, b) => b.retainerSelfSize !== a.retainerSelfSize
|
|
253
|
+
? b.retainerSelfSize - a.retainerSelfSize
|
|
254
|
+
: b.edgesToMatches - a.edgesToMatches);
|
|
255
|
+
return rows.length > MAX_RETAINER_RESULTS ? rows.slice(0, MAX_RETAINER_RESULTS) : rows;
|
|
256
|
+
}
|
|
257
|
+
/** Run a retainer query over a parsed snapshot. Pure — exported for unit
|
|
258
|
+
* tests; the public-facing tool composes parse → query. */
|
|
259
|
+
export function queryRetainers(p, q) {
|
|
260
|
+
if (!q.name && !q.type) {
|
|
261
|
+
// Caller must specify SOMETHING — running against every node would dump
|
|
262
|
+
// millions of edges and is never the right answer.
|
|
263
|
+
throw new Error("heap_retainers: query must specify at least one of `name` or `type`");
|
|
264
|
+
}
|
|
265
|
+
const nodeCount = (p.nodes.length / p.nodeFieldCount) | 0;
|
|
266
|
+
const { matchSet, totalSelfSize } = findMatchingNodes(p, q, nodeCount);
|
|
267
|
+
const summary = {
|
|
268
|
+
nodeCount,
|
|
269
|
+
edgeCount: (p.edges.length / p.edgeFieldCount) | 0,
|
|
270
|
+
stringCount: p.strings.length,
|
|
271
|
+
totalSelfSize,
|
|
272
|
+
};
|
|
273
|
+
if (matchSet.size === 0) {
|
|
274
|
+
return { summary, matchCount: 0, retainers: [], sampleMatches: [] };
|
|
275
|
+
}
|
|
276
|
+
// Sample matches for the caller's sanity check (cap at MAX_SAMPLE_MATCHES).
|
|
277
|
+
const sampleMatches = [];
|
|
278
|
+
for (const idx of matchSet) {
|
|
279
|
+
if (sampleMatches.length >= MAX_SAMPLE_MATCHES)
|
|
280
|
+
break;
|
|
281
|
+
sampleMatches.push(nodeDisplayName(p, idx));
|
|
282
|
+
}
|
|
283
|
+
const retainers = materialiseRetainerRows(p, walkRetainers(p, matchSet, nodeCount));
|
|
284
|
+
return { summary, matchCount: matchSet.size, retainers, sampleMatches };
|
|
285
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Locator } from "playwright-core";
|
|
2
|
+
/** Always-returned style keys — the ones layout/control-state bugs hinge on.
|
|
3
|
+
* Callers can request extra properties via `extra`. */
|
|
4
|
+
export declare const DEFAULT_STYLE_KEYS: readonly ["display", "visibility", "opacity", "position", "cursor", "pointerEvents", "overflow", "overflowX", "overflowY", "zIndex", "flexDirection", "justifyContent", "alignItems"];
|
|
5
|
+
export interface InspectResult {
|
|
6
|
+
found: boolean;
|
|
7
|
+
/** Bounding box in CSS px (raw getBoundingClientRect — not viewport-clipped;
|
|
8
|
+
* use `find()`'s visible-rect bbox when you need the clipped version). */
|
|
9
|
+
box?: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
styles?: Record<string, string>;
|
|
16
|
+
/** True when the element overflows its own padding box on either axis
|
|
17
|
+
* (scrollWidth/Height > clientWidth/Height) — the "label clips / content
|
|
18
|
+
* overflows" signal. */
|
|
19
|
+
overflowing?: {
|
|
20
|
+
x: boolean;
|
|
21
|
+
y: boolean;
|
|
22
|
+
};
|
|
23
|
+
/** Cheap visibility read: non-zero box + not display:none/visibility:hidden
|
|
24
|
+
* + opacity > 0. Not the full visible-rect intersection (that's `find`). */
|
|
25
|
+
visible?: boolean;
|
|
26
|
+
childCount?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read the whitelisted computed styles + box for a resolved element. Pure
|
|
30
|
+
* read (no action window). `extra` appends caller-requested style properties
|
|
31
|
+
* to the default set. Returns `{ found:false }` when the locator matches
|
|
32
|
+
* nothing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function inspectElement(loc: Locator, extra?: string[]): Promise<InspectResult>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
// computed-style + box probe.
|
|
3
|
+
//
|
|
4
|
+
// Layout-break and control-state bugs (a flex row losing a child →
|
|
5
|
+
// misalignment; `cursor-wait` vs `cursor-not-allowed`; a label that clips /
|
|
6
|
+
// overflows) need computed style + box geometry, which the curated surface
|
|
7
|
+
// doesn't otherwise expose. `inspect` is a read primitive returning a
|
|
8
|
+
// *whitelisted* style set + box + overflow/clip state — generalizes the
|
|
9
|
+
// visible-rect work to "is this visually broken".
|
|
10
|
+
/** Always-returned style keys — the ones layout/control-state bugs hinge on.
|
|
11
|
+
* Callers can request extra properties via `extra`. */
|
|
12
|
+
export const DEFAULT_STYLE_KEYS = [
|
|
13
|
+
"display",
|
|
14
|
+
"visibility",
|
|
15
|
+
"opacity",
|
|
16
|
+
"position",
|
|
17
|
+
"cursor",
|
|
18
|
+
"pointerEvents",
|
|
19
|
+
"overflow",
|
|
20
|
+
"overflowX",
|
|
21
|
+
"overflowY",
|
|
22
|
+
"zIndex",
|
|
23
|
+
"flexDirection",
|
|
24
|
+
"justifyContent",
|
|
25
|
+
"alignItems",
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Read the whitelisted computed styles + box for a resolved element. Pure
|
|
29
|
+
* read (no action window). `extra` appends caller-requested style properties
|
|
30
|
+
* to the default set. Returns `{ found:false }` when the locator matches
|
|
31
|
+
* nothing.
|
|
32
|
+
*/
|
|
33
|
+
export async function inspectElement(loc, extra = []) {
|
|
34
|
+
try {
|
|
35
|
+
if ((await loc.count()) === 0)
|
|
36
|
+
return { found: false };
|
|
37
|
+
const keys = [...DEFAULT_STYLE_KEYS, ...extra];
|
|
38
|
+
return await loc
|
|
39
|
+
.evaluate((e, styleKeys) => {
|
|
40
|
+
const cs = getComputedStyle(e);
|
|
41
|
+
const styles = {};
|
|
42
|
+
for (const k of styleKeys) {
|
|
43
|
+
try {
|
|
44
|
+
styles[k] = String(cs[k]);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
/* unknown prop — skip */
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const r = e.getBoundingClientRect();
|
|
51
|
+
const box = { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
52
|
+
const overflowing = {
|
|
53
|
+
x: e.scrollWidth > e.clientWidth + 1,
|
|
54
|
+
y: e.scrollHeight > e.clientHeight + 1,
|
|
55
|
+
};
|
|
56
|
+
const visible = box.width > 0 &&
|
|
57
|
+
box.height > 0 &&
|
|
58
|
+
styles.display !== "none" &&
|
|
59
|
+
styles.visibility !== "hidden" &&
|
|
60
|
+
Number(styles.opacity || "1") > 0;
|
|
61
|
+
return {
|
|
62
|
+
found: true,
|
|
63
|
+
box,
|
|
64
|
+
styles,
|
|
65
|
+
overflowing,
|
|
66
|
+
visible,
|
|
67
|
+
childCount: e.childElementCount,
|
|
68
|
+
};
|
|
69
|
+
}, keys)
|
|
70
|
+
.catch(() => ({ found: false }));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return { found: false };
|
|
74
|
+
}
|
|
75
|
+
}
|