sourcemonkey 0.0.0-stage → 0.5.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 +87 -2
- package/dist/bytes.js +37 -0
- package/dist/channel-placeholder.js +5 -0
- package/dist/collection-scan.js +17 -0
- package/dist/collections.html +353 -0
- package/dist/compare.html +358 -0
- package/dist/composed-source.js +205 -0
- package/dist/connect-prompt.html +47 -0
- package/dist/debug/icons.html +211 -0
- package/dist/debug/storage.html +163 -0
- package/dist/debug-log.js +75 -0
- package/dist/diagnostic-location.js +64 -0
- package/dist/eval-hook.js +308 -0
- package/dist/extension/background.js +5263 -0
- package/dist/extension/channel.js +133 -0
- package/dist/extension/code-links.js +187 -0
- package/dist/extension/code-tabs.js +460 -0
- package/dist/extension/collection-dialog.js +619 -0
- package/dist/extension/collection-display.js +322 -0
- package/dist/extension/collections.js +2905 -0
- package/dist/extension/compare.js +867 -0
- package/dist/extension/concurrency.js +186 -0
- package/dist/extension/connect-grants.js +109 -0
- package/dist/extension/connect-prompt-page.js +70 -0
- package/dist/extension/connect-prompt.js +266 -0
- package/dist/extension/copy-button.js +63 -0
- package/dist/extension/copy-dialog.js +502 -0
- package/dist/extension/copy-preference.js +66 -0
- package/dist/extension/debug/icons.js +203 -0
- package/dist/extension/debug/storage.js +365 -0
- package/dist/extension/deep-link.js +77 -0
- package/dist/extension/delete-dialog.js +432 -0
- package/dist/extension/delete-icon.js +40 -0
- package/dist/extension/details-tooltips.js +66 -0
- package/dist/extension/directories.js +679 -0
- package/dist/extension/export-backup.js +407 -0
- package/dist/extension/export-dialog.js +142 -0
- package/dist/extension/file-access.js +74 -0
- package/dist/extension/file-changes.js +346 -0
- package/dist/extension/frame-info.js +85 -0
- package/dist/extension/github-provider.js +336 -0
- package/dist/extension/glyph.js +84 -0
- package/dist/extension/gm-bindings.js +601 -0
- package/dist/extension/gm-download.js +419 -0
- package/dist/extension/gm-menu.js +228 -0
- package/dist/extension/gm-notification.js +141 -0
- package/dist/extension/gm-originator-map.js +23 -0
- package/dist/extension/gm-tab-data.js +121 -0
- package/dist/extension/gm-tab.js +160 -0
- package/dist/extension/gm-url-change.js +111 -0
- package/dist/extension/gm-url-scheme.js +27 -0
- package/dist/extension/gm-values.js +176 -0
- package/dist/extension/gm-window-control.js +104 -0
- package/dist/extension/gm-xhr-headers.js +333 -0
- package/dist/extension/gm-xhr.js +584 -0
- package/dist/extension/injector.js +3145 -0
- package/dist/extension/install-capture.js +277 -0
- package/dist/extension/line-diff.js +233 -0
- package/dist/extension/local-copy.js +560 -0
- package/dist/extension/local-delete.js +276 -0
- package/dist/extension/location-kinds.js +108 -0
- package/dist/extension/log-format.js +158 -0
- package/dist/extension/managed-changes.js +361 -0
- package/dist/extension/managed-items.js +57 -0
- package/dist/extension/match-index.js +375 -0
- package/dist/extension/missing-scripts.js +66 -0
- package/dist/extension/more-menu.js +109 -0
- package/dist/extension/move-dialog.js +197 -0
- package/dist/extension/offscreen.js +37 -0
- package/dist/extension/options.js +504 -0
- package/dist/extension/page-heading.js +170 -0
- package/dist/extension/page-links.js +199 -0
- package/dist/extension/parse-check-client.js +70 -0
- package/dist/extension/parse-check.js +161 -0
- package/dist/extension/parse-sandbox-host.js +172 -0
- package/dist/extension/parse-sandbox.js +175 -0
- package/dist/extension/popup-commands-open.js +41 -0
- package/dist/extension/popup-view.js +387 -0
- package/dist/extension/popup.js +703 -0
- package/dist/extension/prism-userscript.js +53 -0
- package/dist/extension/refresh-error.js +136 -0
- package/dist/extension/refresh-targets.js +160 -0
- package/dist/extension/related-versions.js +1144 -0
- package/dist/extension/resize-observer-loop.js +19 -0
- package/dist/extension/row-cache.js +387 -0
- package/dist/extension/script-blobs.js +413 -0
- package/dist/extension/script-details.js +658 -0
- package/dist/extension/script-enablement.js +285 -0
- package/dist/extension/script-identity.js +310 -0
- package/dist/extension/script-keys.js +54 -0
- package/dist/extension/script-log-id.js +155 -0
- package/dist/extension/script-log.js +290 -0
- package/dist/extension/script-rejection.js +104 -0
- package/dist/extension/script-sites.js +158 -0
- package/dist/extension/script-store.js +3507 -0
- package/dist/extension/script-updater.js +991 -0
- package/dist/extension/script-url-hash.js +61 -0
- package/dist/extension/select-all.js +37 -0
- package/dist/extension/select-open-guard.js +90 -0
- package/dist/extension/site-icon.js +1261 -0
- package/dist/extension/sm-dev-link.js +302 -0
- package/dist/extension/solo-click.js +58 -0
- package/dist/extension/sticky-numbers.js +188 -0
- package/dist/extension/storage-breakdown.js +253 -0
- package/dist/extension/storage-scan.js +86 -0
- package/dist/extension/storage-usage.js +194 -0
- package/dist/extension/structural-keys.js +29 -0
- package/dist/extension/sw-teardown.js +36 -0
- package/dist/extension/tab-status.js +403 -0
- package/dist/extension/toolbar-summary.js +105 -0
- package/dist/extension/user-scripts-access.js +43 -0
- package/dist/extension/userscript.js +3672 -0
- package/dist/extension/value-store.js +204 -0
- package/dist/extension/version-probe-fetch.js +68 -0
- package/dist/extension/worker-client.js +109 -0
- package/dist/extension/worker-ops.js +171 -0
- package/dist/extension/zip-writer.js +24 -0
- package/dist/external-files.js +878 -0
- package/dist/file-loader.js +436 -0
- package/dist/file-locations.js +439 -0
- package/dist/generated-wrappers.js +5 -0
- package/dist/github-auth.js +148 -0
- package/dist/github-urls.js +46 -0
- package/dist/gm-api-catalog.js +217 -0
- package/dist/gm-connect.js +273 -0
- package/dist/gm-xhr-types.js +5 -0
- package/dist/http-read.js +142 -0
- package/dist/icons/icon-128.png +0 -0
- package/dist/icons/icon-16.png +0 -0
- package/dist/icons/icon-48.png +0 -0
- package/dist/local-file-overrides.js +558 -0
- package/dist/manifest.json +38 -0
- package/dist/markup-response.js +126 -0
- package/dist/match-targets.js +305 -0
- package/dist/menu-captions.js +22 -0
- package/dist/offscreen.html +39 -0
- package/dist/options.html +197 -0
- package/dist/parse-check-protocol.js +51 -0
- package/dist/parse-location.js +171 -0
- package/dist/parse-sandbox.html +56 -0
- package/dist/phase-timing.js +155 -0
- package/dist/popup.html +197 -0
- package/dist/public-suffixes.js +6957 -0
- package/dist/run-context.js +31 -0
- package/dist/runtime-messages.js +34 -0
- package/dist/sandboxed-page.js +35 -0
- package/dist/script-build.js +787 -0
- package/dist/script-display.js +383 -0
- package/dist/script-icon.js +43 -0
- package/dist/script-links.js +204 -0
- package/dist/script-source-url.js +214 -0
- package/dist/script-world.js +65 -0
- package/dist/sm-dev-protocol.js +84 -0
- package/dist/source-map.js +177 -0
- package/dist/stack-link.js +138 -0
- package/dist/styles.css +825 -0
- package/dist/unsafe-window-use.js +229 -0
- package/dist/url-match.js +734 -0
- package/dist/userscript-header.js +248 -0
- package/dist/userscript.html +1165 -0
- package/dist/vendor/fflate.LICENSE +21 -0
- package/dist/vendor/fflate.js +2692 -0
- package/dist/vendor/prism/prism-line-numbers.css +1 -0
- package/dist/vendor/prism/prism-line-numbers.js +1 -0
- package/dist/vendor/prism/prism.css +1 -0
- package/dist/vendor/prism/prism.js +1946 -0
- package/dist/version-compare.js +141 -0
- package/dist/version-probe.js +120 -0
- package/lib/build-info.json +1 -0
- package/lib/bytes.d.ts +3 -0
- package/lib/bytes.js +37 -0
- package/lib/channel-placeholder.d.ts +1 -0
- package/lib/channel-placeholder.js +5 -0
- package/lib/collection-scan.d.ts +8 -0
- package/lib/collection-scan.js +17 -0
- package/lib/composed-source.d.ts +28 -0
- package/lib/composed-source.js +205 -0
- package/lib/debug-log.d.ts +9 -0
- package/lib/debug-log.js +75 -0
- package/lib/diagnostic-location.d.ts +8 -0
- package/lib/diagnostic-location.js +64 -0
- package/lib/eval-hook.d.ts +34 -0
- package/lib/eval-hook.js +308 -0
- package/lib/external-files.d.ts +111 -0
- package/lib/external-files.js +878 -0
- package/lib/file-loader.d.ts +48 -0
- package/lib/file-loader.js +436 -0
- package/lib/file-locations.d.ts +44 -0
- package/lib/file-locations.js +439 -0
- package/lib/generated-wrappers.d.ts +11 -0
- package/lib/generated-wrappers.js +5 -0
- package/lib/github-auth.d.ts +12 -0
- package/lib/github-auth.js +148 -0
- package/lib/github-urls.d.ts +1 -0
- package/lib/github-urls.js +46 -0
- package/lib/gm-api-catalog.d.ts +24 -0
- package/lib/gm-api-catalog.js +217 -0
- package/lib/gm-connect.d.ts +34 -0
- package/lib/gm-connect.js +273 -0
- package/lib/gm-xhr-types.d.ts +68 -0
- package/lib/gm-xhr-types.js +5 -0
- package/lib/harness/browser-extensions.d.ts +73 -0
- package/lib/harness/browser-extensions.js +285 -0
- package/lib/harness/browser-lang.d.ts +20 -0
- package/lib/harness/browser-lang.js +86 -0
- package/lib/harness/browser-launch.d.ts +14 -0
- package/lib/harness/browser-launch.js +184 -0
- package/lib/harness/checks.d.ts +110 -0
- package/lib/harness/checks.js +321 -0
- package/lib/harness/cli.d.ts +2 -0
- package/lib/harness/cli.js +2259 -0
- package/lib/harness/command-args.d.ts +91 -0
- package/lib/harness/command-args.js +369 -0
- package/lib/harness/command-lines.d.ts +80 -0
- package/lib/harness/command-lines.js +180 -0
- package/lib/harness/command-list.d.ts +206 -0
- package/lib/harness/command-list.js +1475 -0
- package/lib/harness/console-commands.d.ts +463 -0
- package/lib/harness/console-commands.js +3216 -0
- package/lib/harness/download-watch.d.ts +21 -0
- package/lib/harness/download-watch.js +156 -0
- package/lib/harness/extension-backend.d.ts +41 -0
- package/lib/harness/extension-backend.js +100 -0
- package/lib/harness/extension-host.d.ts +112 -0
- package/lib/harness/extension-host.js +552 -0
- package/lib/harness/extension-launch.d.ts +67 -0
- package/lib/harness/extension-launch.js +285 -0
- package/lib/harness/extension-link.d.ts +315 -0
- package/lib/harness/extension-link.js +607 -0
- package/lib/harness/external-manager.d.ts +146 -0
- package/lib/harness/external-manager.js +414 -0
- package/lib/harness/fixture.d.ts +165 -0
- package/lib/harness/fixture.js +461 -0
- package/lib/harness/frame-sessions.d.ts +71 -0
- package/lib/harness/frame-sessions.js +248 -0
- package/lib/harness/frames.d.ts +68 -0
- package/lib/harness/frames.js +216 -0
- package/lib/harness/help-format.d.ts +17 -0
- package/lib/harness/help-format.js +127 -0
- package/lib/harness/host.d.ts +352 -0
- package/lib/harness/host.js +423 -0
- package/lib/harness/index.d.ts +8 -0
- package/lib/harness/index.js +10 -0
- package/lib/harness/input-file.d.ts +18 -0
- package/lib/harness/input-file.js +61 -0
- package/lib/harness/json-output.d.ts +565 -0
- package/lib/harness/json-output.js +71 -0
- package/lib/harness/manager-actions.d.ts +21 -0
- package/lib/harness/manager-actions.js +129 -0
- package/lib/harness/manager-commands.d.ts +142 -0
- package/lib/harness/manager-commands.js +1196 -0
- package/lib/harness/manager-fetch.d.ts +33 -0
- package/lib/harness/manager-fetch.js +239 -0
- package/lib/harness/managers.d.ts +24 -0
- package/lib/harness/managers.js +27 -0
- package/lib/harness/output-style.d.ts +125 -0
- package/lib/harness/output-style.js +322 -0
- package/lib/harness/page-commands.d.ts +134 -0
- package/lib/harness/page-commands.js +1141 -0
- package/lib/harness/page-output.d.ts +67 -0
- package/lib/harness/page-output.js +139 -0
- package/lib/harness/page-watch.d.ts +171 -0
- package/lib/harness/page-watch.js +607 -0
- package/lib/harness/parse-check-node.d.ts +3 -0
- package/lib/harness/parse-check-node.js +132 -0
- package/lib/harness/path-args.d.ts +22 -0
- package/lib/harness/path-args.js +148 -0
- package/lib/harness/playwright-chromium.d.ts +16 -0
- package/lib/harness/playwright-chromium.js +86 -0
- package/lib/harness/prepare.d.ts +168 -0
- package/lib/harness/prepare.js +364 -0
- package/lib/harness/prompt-editor.d.ts +17 -0
- package/lib/harness/prompt-editor.js +354 -0
- package/lib/harness/prompt-history.d.ts +13 -0
- package/lib/harness/prompt-history.js +89 -0
- package/lib/harness/rebuild-advice.d.ts +6 -0
- package/lib/harness/rebuild-advice.js +39 -0
- package/lib/harness/run-log.d.ts +47 -0
- package/lib/harness/run-log.js +140 -0
- package/lib/harness/run-logs.d.ts +132 -0
- package/lib/harness/run-logs.js +241 -0
- package/lib/harness/run.d.ts +194 -0
- package/lib/harness/run.js +1973 -0
- package/lib/harness/runs.d.ts +229 -0
- package/lib/harness/runs.js +726 -0
- package/lib/harness/save-html.d.ts +39 -0
- package/lib/harness/save-html.js +89 -0
- package/lib/harness/saved-pages.d.ts +120 -0
- package/lib/harness/saved-pages.js +275 -0
- package/lib/harness/script-mark.d.ts +17 -0
- package/lib/harness/script-mark.js +56 -0
- package/lib/harness/snapshot.d.ts +143 -0
- package/lib/harness/snapshot.js +474 -0
- package/lib/harness/stuck-tabs.d.ts +19 -0
- package/lib/harness/stuck-tabs.js +131 -0
- package/lib/harness/tab-refs.d.ts +20 -0
- package/lib/harness/tab-refs.js +80 -0
- package/lib/harness/tampermonkey.d.ts +71 -0
- package/lib/harness/tampermonkey.js +356 -0
- package/lib/harness/tm-ids.d.ts +18 -0
- package/lib/harness/tm-ids.js +88 -0
- package/lib/harness/violentmonkey.d.ts +34 -0
- package/lib/harness/violentmonkey.js +162 -0
- package/lib/harness/wait.d.ts +77 -0
- package/lib/harness/wait.js +458 -0
- package/lib/http-read.d.ts +30 -0
- package/lib/http-read.js +142 -0
- package/lib/local-file-overrides.d.ts +35 -0
- package/lib/local-file-overrides.js +558 -0
- package/lib/manifest.json +38 -0
- package/lib/markup-response.d.ts +11 -0
- package/lib/markup-response.js +126 -0
- package/lib/match-targets.d.ts +18 -0
- package/lib/match-targets.js +305 -0
- package/lib/menu-captions.d.ts +1 -0
- package/lib/menu-captions.js +22 -0
- package/lib/parse-check-protocol.d.ts +27 -0
- package/lib/parse-check-protocol.js +51 -0
- package/lib/parse-location.d.ts +12 -0
- package/lib/parse-location.js +171 -0
- package/lib/phase-timing.d.ts +12 -0
- package/lib/phase-timing.js +155 -0
- package/lib/public-suffixes.d.ts +1 -0
- package/lib/public-suffixes.js +6957 -0
- package/lib/run-context.d.ts +2 -0
- package/lib/run-context.js +31 -0
- package/lib/runtime-messages.d.ts +10 -0
- package/lib/runtime-messages.js +34 -0
- package/lib/sandboxed-page.d.ts +5 -0
- package/lib/sandboxed-page.js +35 -0
- package/lib/script-build.d.ts +111 -0
- package/lib/script-build.js +787 -0
- package/lib/script-display.d.ts +51 -0
- package/lib/script-display.js +383 -0
- package/lib/script-icon.d.ts +16 -0
- package/lib/script-icon.js +43 -0
- package/lib/script-links.d.ts +24 -0
- package/lib/script-links.js +204 -0
- package/lib/script-source-url.d.ts +8 -0
- package/lib/script-source-url.js +214 -0
- package/lib/script-world.d.ts +6 -0
- package/lib/script-world.js +65 -0
- package/lib/sm-dev-protocol.d.ts +251 -0
- package/lib/sm-dev-protocol.js +84 -0
- package/lib/source-map.d.ts +32 -0
- package/lib/source-map.js +177 -0
- package/lib/stack-link.d.ts +15 -0
- package/lib/stack-link.js +138 -0
- package/lib/unsafe-window-use.d.ts +1 -0
- package/lib/unsafe-window-use.js +229 -0
- package/lib/url-match.d.ts +40 -0
- package/lib/url-match.js +734 -0
- package/lib/userscript-header.d.ts +33 -0
- package/lib/userscript-header.js +248 -0
- package/lib/version-compare.d.ts +6 -0
- package/lib/version-compare.js +141 -0
- package/lib/version-probe.d.ts +17 -0
- package/lib/version-probe.js +120 -0
- package/package.json +55 -3
- package/skills/SourceMonkey-dev/SKILL.md +868 -0
- package/skills/SourceMonkey-dev/references/cdp.md +293 -0
- package/skills/SourceMonkey-dev/references/commands.md +297 -0
- package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
- package/skills/SourceMonkey-dev/references/specs.md +46 -0
- package/skills/SourceMonkey-friction/SKILL.md +103 -0
- package/skills/SourceMonkey-friction/references/template.md +59 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// A throwaway Chromium with the package's built SourceMonkey (dist/)
|
|
2
|
+
// loaded: sm-dev's temp browser, the `sourcemonkey/harness`
|
|
3
|
+
// fixture's, and the repo's own e2e suites'. With `--manager`, a
|
|
4
|
+
// fetched Tampermonkey or Violentmonkey instead (manager-fetch.ts).
|
|
5
|
+
//
|
|
6
|
+
// - Playwright's Chromium, since Google Chrome (137+) ignores
|
|
7
|
+
// `--load-extension`. Hidden runs use Chrome's new headless mode
|
|
8
|
+
// (`--headless=new`), the only headless mode that loads extensions,
|
|
9
|
+
// with a windowed Chrome's user agent (see `windowedUserAgent`).
|
|
10
|
+
// - A fresh profile in a temp directory, deleted when the browser is
|
|
11
|
+
// closed. Its Preferences grant "Allow User Scripts" before launch,
|
|
12
|
+
// which Chrome otherwise leaves to a click (chrome.userScripts is
|
|
13
|
+
// undefined without it), and pin the extension to the toolbar.
|
|
14
|
+
// - Launched as a persistent context (a profile directory needs one),
|
|
15
|
+
// which has no Playwright `Browser`. A caller that needs one (sm-dev's
|
|
16
|
+
// run, the fixture's `browser`) connects over CDP to the launched
|
|
17
|
+
// browser's port; the e2e suites use the context as it is.
|
|
18
|
+
import { execFile } from 'node:child_process';
|
|
19
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import net from 'node:net';
|
|
21
|
+
import os from 'node:os';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
import { leaveDialogsOpen } from './console-commands.js';
|
|
25
|
+
import { ExtensionMissingError, extensionIdFromKey, findExtensionWorker, sourceMonkeyExtensionId } from './extension-backend.js';
|
|
26
|
+
import { fetchedDir } from './manager-fetch.js';
|
|
27
|
+
import { MANAGERS, managerFlag } from './managers.js';
|
|
28
|
+
import { loadPlaywright, missingChromium } from './playwright-chromium.js';
|
|
29
|
+
import { rebuildAdvice } from './rebuild-advice.js';
|
|
30
|
+
import { quoted } from './output-style.js';
|
|
31
|
+
// dist/, beside lib/: in the checkout, and in an installed package,
|
|
32
|
+
// which ships both.
|
|
33
|
+
export const EXTENSION_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'dist');
|
|
34
|
+
/**
|
|
35
|
+
* Launches Chromium with SourceMonkey loaded, or `options.manager`.
|
|
36
|
+
* Throws an error saying why when it can't. It has one blank tab,
|
|
37
|
+
* opened after the warm-up (see `warmUp`), for a run's first tab.
|
|
38
|
+
*/
|
|
39
|
+
export async function launchExtensionBrowser(options = {}) {
|
|
40
|
+
const manager = options.manager ?? 'sourcemonkey';
|
|
41
|
+
const dir = manager === 'sourcemonkey' ? EXTENSION_DIR : fetchedDir(manager);
|
|
42
|
+
if (!existsSync(path.join(dir, 'manifest.json'))) {
|
|
43
|
+
throw new Error(manager === 'sourcemonkey' ? `The built extension is missing from ${EXTENSION_DIR}. ${rebuildAdvice()}`
|
|
44
|
+
: `${MANAGERS[manager].label} isn't fetched. Fetch it with ${quoted(`sm-dev ${managerFlag(manager)} manager fetch`)}`);
|
|
45
|
+
}
|
|
46
|
+
const headed = options.headed === true;
|
|
47
|
+
const profile = mkdtempSync(path.join(os.tmpdir(), 'sm-dev-extension-'));
|
|
48
|
+
let context;
|
|
49
|
+
const close = async () => {
|
|
50
|
+
await context?.close().catch(() => undefined);
|
|
51
|
+
try {
|
|
52
|
+
rmSync(profile, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
catch { /* Chrome may hold a file briefly */ }
|
|
55
|
+
};
|
|
56
|
+
try {
|
|
57
|
+
if (manager === 'sourcemonkey')
|
|
58
|
+
await prepareProfile(profile, options.downloadsDir);
|
|
59
|
+
else
|
|
60
|
+
writePreferences(profile, fetchedId(dir, manager), options.downloadsDir);
|
|
61
|
+
// Port 0 has Chrome pick a free port and write it to the profile.
|
|
62
|
+
// A port picked here could be taken before Chrome binds it, and the
|
|
63
|
+
// run would connect to whatever took it. A port given is checked
|
|
64
|
+
// first, since Chrome starts without its port when it's taken.
|
|
65
|
+
if (options.port !== undefined && !await portFree(options.port)) {
|
|
66
|
+
throw new Error(`Port ${options.port} is already in use. Use ${quoted('--existing-browser')} to connect to a browser there, or pick another port.`);
|
|
67
|
+
}
|
|
68
|
+
context = await launch(profile, headed, [`--remote-debugging-port=${options.port ?? 0}`, ...(options.muted === false ? [] : ['--mute-audio']), ...(options.args ?? [])], dir, options.backForwardCache === true);
|
|
69
|
+
// Chrome writes the port it picked; one given, it doesn't.
|
|
70
|
+
const port = options.port ?? readFileSync(path.join(profile, 'DevToolsActivePort'), 'utf8').split('\n')[0].trim();
|
|
71
|
+
if (options.connectedElsewhere)
|
|
72
|
+
leaveDialogsOpen(context);
|
|
73
|
+
if (options.downloadsDir)
|
|
74
|
+
await chromeDownloads(context);
|
|
75
|
+
if (manager === 'sourcemonkey')
|
|
76
|
+
await warmUp(context);
|
|
77
|
+
else if (manager === 'tampermonkey')
|
|
78
|
+
await quietTampermonkey(context, fetchedId(dir, manager));
|
|
79
|
+
// The launch's first tab predates the warm-up, so scripts fail in
|
|
80
|
+
// it. A new one replaces it (opened first: a headed browser with
|
|
81
|
+
// no tab left would quit).
|
|
82
|
+
const stale = context.pages();
|
|
83
|
+
await context.newPage();
|
|
84
|
+
for (const page of stale)
|
|
85
|
+
await page.close();
|
|
86
|
+
return { endpoint: `http://127.0.0.1:${port}`, context, profile, close };
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
await close();
|
|
90
|
+
if (err instanceof ExtensionMissingError)
|
|
91
|
+
throw new Error(`Chromium did not load the extension from ${EXTENSION_DIR}`);
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Whether nothing listens on `port` on this machine.
|
|
96
|
+
function portFree(port) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
const server = net.createServer();
|
|
99
|
+
server.once('error', () => resolve(false));
|
|
100
|
+
server.listen(port, '127.0.0.1', () => server.close(() => resolve(true)));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// The user agent a windowed Chrome of this version sends, in Chrome's
|
|
104
|
+
// reduced form (`Chrome/153.0.0.0`). A hidden one says HeadlessChrome,
|
|
105
|
+
// which some servers refuse, and the extension fetches a script's
|
|
106
|
+
// `@require`s and `GM_xmlhttpRequest`s with it. Only the user agent
|
|
107
|
+
// string changes: the client hints (`Sec-CH-UA`,
|
|
108
|
+
// `navigator.userAgentData`) still name HeadlessChrome. Null when the
|
|
109
|
+
// version can't be read; the browser's own is kept then.
|
|
110
|
+
async function windowedUserAgent(executable) {
|
|
111
|
+
const version = process.platform === 'win32'
|
|
112
|
+
// There `chrome.exe --version` opens a window instead of printing;
|
|
113
|
+
// the version is the name of a folder beside it.
|
|
114
|
+
? readdirSync(path.dirname(executable)).find((name) => /^\d+\.\d+\.\d+\.\d+$/.test(name)) ?? ''
|
|
115
|
+
: await new Promise((resolve) => {
|
|
116
|
+
execFile(executable, ['--version'], { timeout: 5000 }, (err, stdout) => resolve(err ? '' : stdout));
|
|
117
|
+
});
|
|
118
|
+
const major = /(\d+)\.\d+\.\d+\.\d+/.exec(version)?.[1];
|
|
119
|
+
if (!major)
|
|
120
|
+
return null;
|
|
121
|
+
const platform = process.platform === 'darwin' ? 'Macintosh; Intel Mac OS X 10_15_7'
|
|
122
|
+
: process.platform === 'win32' ? 'Windows NT 10.0; Win64; x64'
|
|
123
|
+
: 'X11; Linux x86_64';
|
|
124
|
+
return `Mozilla/5.0 (${platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${major}.0.0.0 Safari/537.36`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Turns off Tampermonkey's "installed" page, which it opens in a new
|
|
128
|
+
* tab a second or so after each start of a copy loaded with
|
|
129
|
+
* `--load-extension` (Chrome installs it anew each time): its
|
|
130
|
+
* `notification_showUpdate` setting, set before it gets that far.
|
|
131
|
+
*/
|
|
132
|
+
async function quietTampermonkey(context, id) {
|
|
133
|
+
const ours = (w) => w.url().startsWith(`chrome-extension://${id}/`);
|
|
134
|
+
const worker = context.serviceWorkers().find(ours) ?? await context.waitForEvent('serviceworker', { predicate: ours, timeout: 10_000 });
|
|
135
|
+
await worker.evaluate(async () => {
|
|
136
|
+
const key = '!extdb.#config';
|
|
137
|
+
const config = (await chrome.storage.local.get(key))[key] ?? { origin: 'normal', value: {} };
|
|
138
|
+
config.value.notification_showUpdate = 'off';
|
|
139
|
+
await chrome.storage.local.set({ [key]: config });
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// The id of a fetched manager, from the key its manifest was given.
|
|
143
|
+
function fetchedId(dir, manager) {
|
|
144
|
+
const { key } = JSON.parse(readFileSync(path.join(dir, 'manifest.json'), 'utf8'));
|
|
145
|
+
if (typeof key !== 'string')
|
|
146
|
+
throw new Error(`The extension in ${dir} has no key in its manifest. Fetch it again with ${quoted(`sm-dev --manager ${MANAGERS[manager].short} manager fetch`)}`);
|
|
147
|
+
return extensionIdFromKey(key);
|
|
148
|
+
}
|
|
149
|
+
async function launch(profile, headed, args = [], dir = EXTENSION_DIR, backForwardCache = false) {
|
|
150
|
+
const { chromium, install } = await loadPlaywright();
|
|
151
|
+
// Playwright's own message names only the missing path.
|
|
152
|
+
const missing = missingChromium(chromium.executablePath(), install);
|
|
153
|
+
if (missing)
|
|
154
|
+
throw new Error(missing);
|
|
155
|
+
const userAgent = headed ? null : await windowedUserAgent(chromium.executablePath()).catch(() => null);
|
|
156
|
+
try {
|
|
157
|
+
return await chromium.launchPersistentContext(profile, {
|
|
158
|
+
// `headless: false` keeps Playwright from adding its own headless
|
|
159
|
+
// flag (the old mode, which doesn't load extensions); a hidden
|
|
160
|
+
// browser gets `--headless=new` instead.
|
|
161
|
+
headless: false,
|
|
162
|
+
// The run closes it as it stops, on the signals it handles itself.
|
|
163
|
+
// Playwright's own handlers would close it first, and the run would
|
|
164
|
+
// report the browser gone. It still closes it if sm-dev exits.
|
|
165
|
+
handleSIGINT: false,
|
|
166
|
+
handleSIGTERM: false,
|
|
167
|
+
handleSIGHUP: false,
|
|
168
|
+
// A visible browser gets no emulated viewport, so its windows are
|
|
169
|
+
// sized as in a browser a person runs. Playwright's default (a
|
|
170
|
+
// fixed 1280x720) resizes every window it sees in a visible browser
|
|
171
|
+
// to fit, popups such as SourceMonkey's connection prompt included.
|
|
172
|
+
// A hidden one keeps the default: nobody sees its windows, and its
|
|
173
|
+
// pages keep the size they always had.
|
|
174
|
+
...(headed ? { viewport: null } : {}),
|
|
175
|
+
...(backForwardCache ? { ignoreDefaultArgs: ['--disable-back-forward-cache'] } : {}),
|
|
176
|
+
args: [
|
|
177
|
+
...(headed ? [] : ['--headless=new']),
|
|
178
|
+
...(userAgent ? [`--user-agent=${userAgent}`] : []),
|
|
179
|
+
...extensionArgs(dir),
|
|
180
|
+
...args,
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
// Playwright's message starts with the call's name, and may go on
|
|
186
|
+
// for lines (a boxed install hint); the first line says what failed.
|
|
187
|
+
const first = err.message.split('\n')[0].replace(/^browserType\.\w+: /, '');
|
|
188
|
+
throw new Error(`Chromium could not start: ${first}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Installs a script and removes it again, before the run opens its tab.
|
|
193
|
+
*
|
|
194
|
+
* - In a fresh profile, a tab opened before the extension's first script
|
|
195
|
+
* install can't message the extension, even after a reload: a script
|
|
196
|
+
* that runs there reports that it couldn't read its stored GM values.
|
|
197
|
+
* Only a tab in a new renderer process (another site) recovers.
|
|
198
|
+
* - The run pushes its scripts after its tab is open, so it needs a tab
|
|
199
|
+
* opened after this. See TODO.md.
|
|
200
|
+
*/
|
|
201
|
+
export async function warmUp(context) {
|
|
202
|
+
const worker = await findExtensionWorker(context);
|
|
203
|
+
await worker.evaluate(async (text) => {
|
|
204
|
+
const api = self.SourceMonkey;
|
|
205
|
+
const run = 'sm-dev-warm-up';
|
|
206
|
+
await api.writeRunScript({ run, text });
|
|
207
|
+
await api.releaseRun(run);
|
|
208
|
+
}, '// ==UserScript==\n// @name sm-dev warm-up\n// @match https://sm-dev.invalid/*\n// ==/UserScript==\n');
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Readies a new profile for SourceMonkey (Preferences, first run done),
|
|
212
|
+
* for a browser started with `extensionArgs`. Also for a test's own
|
|
213
|
+
* browser, launched outside Playwright.
|
|
214
|
+
*/
|
|
215
|
+
export async function prepareProfile(profile, downloadsDir) {
|
|
216
|
+
writePreferences(profile, await sourceMonkeyExtensionId(), downloadsDir);
|
|
217
|
+
await primeFirstRun(profile);
|
|
218
|
+
}
|
|
219
|
+
/** The Chrome flags that load the package's SourceMonkey (or the extension in `dir`), and only it. */
|
|
220
|
+
export function extensionArgs(dir = EXTENSION_DIR) {
|
|
221
|
+
return [`--disable-extensions-except=${dir}`, `--load-extension=${dir}`];
|
|
222
|
+
}
|
|
223
|
+
// Grants the extension "Allow User Scripts" and pins it to the toolbar,
|
|
224
|
+
// with the keys Chrome writes when those are clicked. With
|
|
225
|
+
// `downloadsDir`, also the keys Chrome writes when the download
|
|
226
|
+
// location is changed in its settings: `directory_upgrade` marks it
|
|
227
|
+
// user-chosen, so Chrome doesn't fall back to its platform default.
|
|
228
|
+
function writePreferences(profile, extensionId, downloadsDir) {
|
|
229
|
+
const dir = path.join(profile, 'Default');
|
|
230
|
+
mkdirSync(dir, { recursive: true });
|
|
231
|
+
const prefs = {
|
|
232
|
+
extensions: { settings: { [extensionId]: { user_scripts_enabled: true } }, pinned_extensions: [extensionId] },
|
|
233
|
+
...(downloadsDir ? { download: { default_directory: downloadsDir, directory_upgrade: true, prompt_for_download: false } } : {}),
|
|
234
|
+
};
|
|
235
|
+
writeFileSync(path.join(dir, 'Preferences'), JSON.stringify(prefs));
|
|
236
|
+
}
|
|
237
|
+
// Hands downloads back to Chrome. Playwright has told it to save every
|
|
238
|
+
// download under a GUID name in its own temp dir (CDP
|
|
239
|
+
// `Browser.setDownloadBehavior: allowAndName`), with no option to opt
|
|
240
|
+
// out. `default` undoes that, so the Preferences above apply.
|
|
241
|
+
async function chromeDownloads(context) {
|
|
242
|
+
const page = await context.newPage();
|
|
243
|
+
try {
|
|
244
|
+
const cdp = await context.newCDPSession(page);
|
|
245
|
+
await cdp.send('Browser.setDownloadBehavior', { behavior: 'default' });
|
|
246
|
+
await cdp.detach();
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
await page.close();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Marks the extension's first run done, in a launch of its own before
|
|
254
|
+
* the run's.
|
|
255
|
+
*
|
|
256
|
+
* - On a first run, the extension opens its collections page, which adds
|
|
257
|
+
* the default collections from GitHub. Their scripts would then run
|
|
258
|
+
* alongside the one being tried.
|
|
259
|
+
* - That starts while the browser is still starting, so the flag can't
|
|
260
|
+
* be set in time in the run's own launch. Set here, it's in storage
|
|
261
|
+
* for the next launch, whose first-run check then does nothing.
|
|
262
|
+
* - This launch may still have started the page's adds, so it closes the
|
|
263
|
+
* page and removes what they added.
|
|
264
|
+
*/
|
|
265
|
+
async function primeFirstRun(profile) {
|
|
266
|
+
const context = await launch(profile, false);
|
|
267
|
+
try {
|
|
268
|
+
const worker = await findExtensionWorker(context);
|
|
269
|
+
// Key mirrors FIRST_RUN_DONE_KEY in src/extension/script-store.ts.
|
|
270
|
+
await worker.evaluate(() => chrome.storage.local.set({ firstRunComplete: true }));
|
|
271
|
+
for (const page of context.pages()) {
|
|
272
|
+
if (page.url().includes('/collections.html'))
|
|
273
|
+
await page.close().catch(() => undefined);
|
|
274
|
+
}
|
|
275
|
+
await worker.evaluate(async () => {
|
|
276
|
+
const sm = self.SourceMonkey;
|
|
277
|
+
for (const c of await sm.getCollections())
|
|
278
|
+
await sm.removeCollection(c.id);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
// A clean close is what writes the storage to disk.
|
|
283
|
+
await context.close();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from '@playwright/test';
|
|
2
|
+
import { type ProtocolVersion } from '../sm-dev-protocol.js';
|
|
3
|
+
import type { StackLayout } from '../composed-source.js';
|
|
4
|
+
import type { PushedFile } from '../external-files.js';
|
|
5
|
+
import { type LogEntry, type PushResult } from './extension-backend.js';
|
|
6
|
+
import type { ManagerControl } from './manager-commands.js';
|
|
7
|
+
import type { ExtensionFrame } from './frames.js';
|
|
8
|
+
/**
|
|
9
|
+
* How the extension's protocol version compares with sm-dev's
|
|
10
|
+
* (`SM_DEV_PROTOCOL`): an error for a major that differs, a note for a
|
|
11
|
+
* minor that does, or null when they match.
|
|
12
|
+
*/
|
|
13
|
+
export declare function compareProtocol(extension: ProtocolVersion | undefined, ours?: ProtocolVersion): {
|
|
14
|
+
error?: string;
|
|
15
|
+
note?: string;
|
|
16
|
+
} | null;
|
|
17
|
+
/** An event from the extension, as sm-dev-link.ts and background.ts send them. */
|
|
18
|
+
export type ExtensionEvent = {
|
|
19
|
+
kind: 'report';
|
|
20
|
+
type: string;
|
|
21
|
+
tabId: number;
|
|
22
|
+
documentId: string;
|
|
23
|
+
topFrame?: boolean;
|
|
24
|
+
scriptId: string;
|
|
25
|
+
name: string;
|
|
26
|
+
url: string;
|
|
27
|
+
message?: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
reason?: string;
|
|
30
|
+
detail?: string;
|
|
31
|
+
at: number;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'log';
|
|
34
|
+
scriptId: string;
|
|
35
|
+
entry: LogEntry;
|
|
36
|
+
fromReport?: boolean;
|
|
37
|
+
at: number;
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'menu';
|
|
40
|
+
op: 'add' | 'remove';
|
|
41
|
+
tabId: number;
|
|
42
|
+
documentId: string;
|
|
43
|
+
topFrame?: boolean;
|
|
44
|
+
scriptId: string;
|
|
45
|
+
commandId: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
at: number;
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'values';
|
|
50
|
+
scriptId: string;
|
|
51
|
+
tabId?: number;
|
|
52
|
+
documentId?: string;
|
|
53
|
+
writes: {
|
|
54
|
+
key: string;
|
|
55
|
+
value?: unknown;
|
|
56
|
+
deleted?: boolean;
|
|
57
|
+
}[];
|
|
58
|
+
at: number;
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'tab-opened';
|
|
61
|
+
sourceTabId: number;
|
|
62
|
+
tabId: number;
|
|
63
|
+
url: string;
|
|
64
|
+
scriptId?: string;
|
|
65
|
+
requestId?: string;
|
|
66
|
+
active?: boolean;
|
|
67
|
+
at: number;
|
|
68
|
+
} | {
|
|
69
|
+
kind: 'tab-closed';
|
|
70
|
+
scriptId: string;
|
|
71
|
+
requestId: string;
|
|
72
|
+
at: number;
|
|
73
|
+
} | {
|
|
74
|
+
kind: 'request';
|
|
75
|
+
scriptId: string;
|
|
76
|
+
tabId: number;
|
|
77
|
+
documentId?: string;
|
|
78
|
+
id: string;
|
|
79
|
+
url: string;
|
|
80
|
+
method: string;
|
|
81
|
+
outcome?: 'load' | 'error' | 'timeout' | 'abort';
|
|
82
|
+
status?: number;
|
|
83
|
+
error?: string;
|
|
84
|
+
at: number;
|
|
85
|
+
} | {
|
|
86
|
+
kind: 'download';
|
|
87
|
+
scriptId: string;
|
|
88
|
+
tabId: number;
|
|
89
|
+
documentId?: string;
|
|
90
|
+
requestId: string;
|
|
91
|
+
url: string;
|
|
92
|
+
name: string;
|
|
93
|
+
outcome?: 'load' | 'error' | 'timeout' | 'abort';
|
|
94
|
+
filename?: string;
|
|
95
|
+
error?: string;
|
|
96
|
+
at: number;
|
|
97
|
+
} | {
|
|
98
|
+
kind: 'connect-prompt';
|
|
99
|
+
op: 'shown' | 'answered' | 'closed';
|
|
100
|
+
promptId: string;
|
|
101
|
+
scriptId: string;
|
|
102
|
+
tabId: number;
|
|
103
|
+
host: string;
|
|
104
|
+
url?: string;
|
|
105
|
+
method?: string;
|
|
106
|
+
answer?: 'once' | 'cancel' | 'allow' | 'allow-all' | 'deny';
|
|
107
|
+
at: number;
|
|
108
|
+
} | {
|
|
109
|
+
kind: 'notification';
|
|
110
|
+
op: 'shown' | 'clicked' | 'closed';
|
|
111
|
+
scriptId: string;
|
|
112
|
+
tabId: number;
|
|
113
|
+
documentId?: string;
|
|
114
|
+
requestId: string;
|
|
115
|
+
notificationId?: string;
|
|
116
|
+
text?: string;
|
|
117
|
+
title?: string;
|
|
118
|
+
at: number;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* A run's id in the extension: sm-dev's run id (`D2`, or a named one)
|
|
122
|
+
* with a random tail, since run ids are reused once a run ends. Kept to
|
|
123
|
+
* the characters a URL path segment allows, since its scripts' URLs
|
|
124
|
+
* carry it.
|
|
125
|
+
*/
|
|
126
|
+
export declare function linkRunId(runId: string | undefined): string;
|
|
127
|
+
export declare class ExtensionLink {
|
|
128
|
+
private readonly context;
|
|
129
|
+
/**
|
|
130
|
+
* The run's unique id (`linkRunId`), which its claims and its test
|
|
131
|
+
* collection carry.
|
|
132
|
+
*/
|
|
133
|
+
readonly runId: string;
|
|
134
|
+
/** The extension's service worker, whose console carries the events. */
|
|
135
|
+
readonly workerUrl: string;
|
|
136
|
+
/** The origin of the extension's pages and its scripts' world. */
|
|
137
|
+
readonly origin: string;
|
|
138
|
+
private worker;
|
|
139
|
+
private readonly heard;
|
|
140
|
+
private readonly stopped;
|
|
141
|
+
private readonly listeners;
|
|
142
|
+
private readonly restartListeners;
|
|
143
|
+
private renewTimer;
|
|
144
|
+
private readonly tabs;
|
|
145
|
+
private readonly vouches;
|
|
146
|
+
private closed;
|
|
147
|
+
private closing;
|
|
148
|
+
private readonly renewing;
|
|
149
|
+
private adopted;
|
|
150
|
+
/** The extension's protocol version, as `open` read it. */
|
|
151
|
+
private extensionProtocol;
|
|
152
|
+
/** For the run to print: how the extension's protocol minor differs from sm-dev's, if it does. */
|
|
153
|
+
versionNote: string | null;
|
|
154
|
+
private absent;
|
|
155
|
+
private madeUpTabId;
|
|
156
|
+
private readonly onWorker;
|
|
157
|
+
/**
|
|
158
|
+
* Throws, naming `feature`, when the extension's protocol minor is
|
|
159
|
+
* older than `minor`: for a call or event added in that minor, before
|
|
160
|
+
* using it.
|
|
161
|
+
*/
|
|
162
|
+
requireMinor(minor: number, feature: string): void;
|
|
163
|
+
/**
|
|
164
|
+
* Whether this run has a SourceMonkey link: for output that depends
|
|
165
|
+
* on what the extension does. False for a run with another manager.
|
|
166
|
+
*/
|
|
167
|
+
get linked(): boolean;
|
|
168
|
+
private constructor();
|
|
169
|
+
/**
|
|
170
|
+
* Connects to the extension in `context`'s browser, for a run. Its
|
|
171
|
+
* tabs run its own scripts, and it hears the extension's events.
|
|
172
|
+
* Throws when the extension's protocol major differs from sm-dev's
|
|
173
|
+
* (`compareProtocol`), or `userScriptsOff` when Chrome won't let it
|
|
174
|
+
* run scripts. A minor that differs leaves `versionNote` set.
|
|
175
|
+
*/
|
|
176
|
+
static open(context: BrowserContext, runId: string): Promise<ExtensionLink>;
|
|
177
|
+
/**
|
|
178
|
+
* A link for a run whose manager isn't SourceMonkey: no extension to
|
|
179
|
+
* call, no tabs claimed, no events heard. Its tabs are plain browser
|
|
180
|
+
* tabs, and every call into the extension throws `reason`.
|
|
181
|
+
*/
|
|
182
|
+
static without(context: BrowserContext, runId: string, reason: string): ExtensionLink;
|
|
183
|
+
private adopt;
|
|
184
|
+
/** Hears each event the extension sends. */
|
|
185
|
+
onEvent(listener: (event: ExtensionEvent) => void): void;
|
|
186
|
+
/**
|
|
187
|
+
* Told when a new worker replaced the one before, whose events in
|
|
188
|
+
* between are lost, with how many of the run's tab claims had to be
|
|
189
|
+
* made again.
|
|
190
|
+
*/
|
|
191
|
+
onRestart(listener: (restoredClaims: number) => void): void;
|
|
192
|
+
private current;
|
|
193
|
+
/**
|
|
194
|
+
* Runs `fn` in the worker. Tried again once in a new worker when the
|
|
195
|
+
* one known stopped meanwhile; an error of the call's own is thrown,
|
|
196
|
+
* not repeated, since a call may not be safe to make twice.
|
|
197
|
+
*/
|
|
198
|
+
call<A, R>(fn: (arg: A) => R | Promise<R>, arg: A): Promise<R>;
|
|
199
|
+
/**
|
|
200
|
+
* Claims `page`'s tab for the run before it loads the run's page. The
|
|
201
|
+
* tab is named to the extension by a navigation it watches for, to
|
|
202
|
+
* `about:blank#<fragment>`, and the claim is recorded before this
|
|
203
|
+
* returns. Leaves the tab on `about:blank`, for the run to load its
|
|
204
|
+
* page in. Returns the tab's id. Throws an error naming the step that
|
|
205
|
+
* failed. A link `without` SourceMonkey claims nothing, and makes up
|
|
206
|
+
* an id.
|
|
207
|
+
*/
|
|
208
|
+
claimPage(page: Page, scriptsOff?: boolean): Promise<number>;
|
|
209
|
+
private nameTab;
|
|
210
|
+
/**
|
|
211
|
+
* A tab the extension claimed for the run itself (one a run tab
|
|
212
|
+
* opened), for renewals to keep.
|
|
213
|
+
*/
|
|
214
|
+
holdTab(tabId: number, scriptsOff?: boolean): void;
|
|
215
|
+
/** The run no longer holds the tab (it closed). */
|
|
216
|
+
forgetTab(tabId: number): void;
|
|
217
|
+
/**
|
|
218
|
+
* Marks a tab the run claimed, for every document it loads from now
|
|
219
|
+
* on, as one whose scripts on the run's own get `run`, or takes the
|
|
220
|
+
* marker away. Through a CDP script, which runs ahead of every
|
|
221
|
+
* extension script in a new document. It only reaches the tab: the
|
|
222
|
+
* tabs it opens, and its subframes, ask the extension. The marker is
|
|
223
|
+
* gone with the session, as the run closes or its connection drops.
|
|
224
|
+
*/
|
|
225
|
+
private setVouch;
|
|
226
|
+
/** Turns a claimed tab's scripts off or back on, for `--noscript` tabs. */
|
|
227
|
+
setTabScriptsOff(tabId: number, off: boolean): Promise<void>;
|
|
228
|
+
private forgetVouch;
|
|
229
|
+
private renew;
|
|
230
|
+
private renewIn;
|
|
231
|
+
/**
|
|
232
|
+
* Installs or replaces one of the run's scripts in its test collection,
|
|
233
|
+
* with the dependency bodies read from disk (`pushedFiles`).
|
|
234
|
+
*/
|
|
235
|
+
/**
|
|
236
|
+
* `source`: the file's `file://` URL, which `script list` shows. An
|
|
237
|
+
* extension before protocol 1.2 ignores it.
|
|
238
|
+
*/
|
|
239
|
+
push(text: string, name: string, files?: Record<string, PushedFile>, source?: string): Promise<PushResult>;
|
|
240
|
+
/** Which document `tabId` shows (see the worker's `tabDocument`), or null if it can't be read. */
|
|
241
|
+
tabDocument(tabId: number): Promise<{
|
|
242
|
+
url: string;
|
|
243
|
+
timeOrigin: number;
|
|
244
|
+
} | null>;
|
|
245
|
+
/** How the script's injected entry maps back to its files; see `remapStackText`. */
|
|
246
|
+
stackLayout(scriptId: string): Promise<StackLayout | null>;
|
|
247
|
+
/**
|
|
248
|
+
* A tab's frames, live documents only, as Chrome lists them to the
|
|
249
|
+
* extension: straight from `chrome.webNavigation`, which every
|
|
250
|
+
* extension sm-dev works with has, so no protocol version gates it.
|
|
251
|
+
* Null when Chrome can't list them (the tab is gone).
|
|
252
|
+
*/
|
|
253
|
+
allFrames(tabId: number): Promise<ExtensionFrame[] | null>;
|
|
254
|
+
/** A script's stored values, in the wire form the extension keeps. */
|
|
255
|
+
values(scriptId: string): Promise<Record<string, unknown>>;
|
|
256
|
+
/** A script's Log entries, oldest first, as its Log tab shows them. */
|
|
257
|
+
scriptLog(scriptId: string): Promise<LogEntry[]>;
|
|
258
|
+
/** Writes values (wire form) as another tab of the script would. */
|
|
259
|
+
writeValues(scriptId: string, writes: {
|
|
260
|
+
key: string;
|
|
261
|
+
value?: unknown;
|
|
262
|
+
deleted?: boolean;
|
|
263
|
+
}[]): Promise<void>;
|
|
264
|
+
/** The scripts the toolbar badge lists for a tab, with their status and collection. */
|
|
265
|
+
scriptsOnTab(tabId: number): Promise<{
|
|
266
|
+
id: string;
|
|
267
|
+
name: string;
|
|
268
|
+
status: string;
|
|
269
|
+
collection: string;
|
|
270
|
+
number?: number;
|
|
271
|
+
}[]>;
|
|
272
|
+
/**
|
|
273
|
+
* Fires a menu command in a tab, as clicking it in the toolbar menu
|
|
274
|
+
* does: in `documentId`, the document that registered it, when given.
|
|
275
|
+
* An extension before protocol 2.1 ignores it, firing the first match.
|
|
276
|
+
*/
|
|
277
|
+
fireMenu(tabId: number, scriptId: string, commandId: string, documentId?: string): Promise<boolean>;
|
|
278
|
+
/**
|
|
279
|
+
* Runs a manual script (`@run-at context-menu`) in a tab, as its popup
|
|
280
|
+
* "Run this script" does. Throws with the extension's reason when it
|
|
281
|
+
* can't.
|
|
282
|
+
*/
|
|
283
|
+
runManualScript(tabId: number, scriptId: string): Promise<void>;
|
|
284
|
+
/** The collections and installed scripts, for the `collection` and `script` commands. */
|
|
285
|
+
readonly manager: ManagerControl;
|
|
286
|
+
/** Clicks a shown notification, as the person would. False when it isn't showing. */
|
|
287
|
+
clickNotification(notificationId: string): Promise<boolean>;
|
|
288
|
+
/**
|
|
289
|
+
* Answers a connection prompt, as the person would in its window.
|
|
290
|
+
* False when it isn't waiting.
|
|
291
|
+
*/
|
|
292
|
+
answerConnectPrompt(promptId: string, choice: 'once' | 'cancel' | 'allow' | 'allow-all' | 'deny'): Promise<boolean>;
|
|
293
|
+
/** Closes a shown notification, as the person would. */
|
|
294
|
+
closeNotification(notificationId: string): Promise<boolean>;
|
|
295
|
+
/**
|
|
296
|
+
* Releases the run's claims and subscription, which removes its test
|
|
297
|
+
* collection, and stops renewing.
|
|
298
|
+
*/
|
|
299
|
+
close(): Promise<void>;
|
|
300
|
+
private release;
|
|
301
|
+
}
|
|
302
|
+
/** An error's first line, without the call Playwright names at its start. */
|
|
303
|
+
export declare function firstLine(err: unknown): string;
|
|
304
|
+
/**
|
|
305
|
+
* The collection and script commands' connection to SourceMonkey, with
|
|
306
|
+
* no run: no tabs claimed, no events heard. For one command, made just
|
|
307
|
+
* after the worker is found, so it doesn't look for a new one.
|
|
308
|
+
* - Throws as `ExtensionLink.open` does for a protocol major that
|
|
309
|
+
* differs.
|
|
310
|
+
* - Doesn't need "Allow User Scripts", since nothing here runs a script.
|
|
311
|
+
*/
|
|
312
|
+
export declare function openManagerLink(context: BrowserContext): Promise<{
|
|
313
|
+
manager: ManagerControl;
|
|
314
|
+
versionNote: string | null;
|
|
315
|
+
}>;
|