package-registry-manager 0.22.3 → 0.22.4
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/package.json +7 -5
- package/src/automation.mjs +21 -11
- package/src/browser-catalogue.mjs +81 -5
- package/src/browser-options.mjs +30 -1
- package/src/cli.mjs +6 -2
- package/src/profile.mjs +10 -3
- package/src/webdriver-automation.mjs +125 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "package-registry-manager",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.4",
|
|
4
4
|
"description": "Inspect repositories and guide secure package-registry setup",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"provenance": true
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
|
-
"syntax": "node --check src/cli.mjs && node --check src/direct-execution.mjs && node --check src/index.mjs && node --check src/browser.mjs && node --check src/browser-options.mjs && node --check src/browser-catalogue.mjs && node --check src/automation.mjs && node --check src/profile.mjs && node --check src/setup.mjs && node --check src/auth-urls.mjs && node --check src/containers.mjs && node --check src/flows.mjs && node --check src/prerequisites.mjs && node --check src/registry-state.mjs && node --check src/workflows.mjs && node --check src/publishers.mjs && node --check src/skips.mjs && node --check src/tokens.mjs && node --check src/approvals.mjs && node --check src/default-browser.mjs && node --check src/pages.mjs && node --check src/crates-api.mjs && node --check src/sign-in-import.mjs && node --check scripts/publish-to-npm.mjs && node --check scripts/setup-npm.mjs && node --check scripts/sanitize-npm-userconfig.mjs",
|
|
31
|
+
"syntax": "node --check src/cli.mjs && node --check src/direct-execution.mjs && node --check src/index.mjs && node --check src/browser.mjs && node --check src/browser-options.mjs && node --check src/browser-catalogue.mjs && node --check src/automation.mjs && node --check src/webdriver-automation.mjs && node --check src/profile.mjs && node --check src/setup.mjs && node --check src/auth-urls.mjs && node --check src/containers.mjs && node --check src/flows.mjs && node --check src/prerequisites.mjs && node --check src/registry-state.mjs && node --check src/workflows.mjs && node --check src/publishers.mjs && node --check src/skips.mjs && node --check src/tokens.mjs && node --check src/approvals.mjs && node --check src/default-browser.mjs && node --check src/pages.mjs && node --check src/crates-api.mjs && node --check src/sign-in-import.mjs && node --check scripts/publish-to-npm.mjs && node --check scripts/setup-npm.mjs && node --check scripts/sanitize-npm-userconfig.mjs",
|
|
32
32
|
"lint": "eslint src test scripts --max-warnings 0",
|
|
33
33
|
"lint:fix": "eslint src test scripts --fix --max-warnings 0",
|
|
34
34
|
"format": "prettier --write .",
|
|
@@ -36,12 +36,14 @@
|
|
|
36
36
|
"check:duplication": "jscpd src",
|
|
37
37
|
"check": "npm run syntax && npm run lint && npm run format:check && npm run check:duplication",
|
|
38
38
|
"test": "node --test --test-timeout=30000",
|
|
39
|
-
"pack:check": "npm pack --dry-run"
|
|
39
|
+
"pack:check": "npm pack --dry-run",
|
|
40
|
+
"audit": "node scripts/npm-audit.mjs"
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
43
|
"browser-commander": "^0.25.0",
|
|
43
|
-
"command-stream": "^1.
|
|
44
|
-
"playwright": "^1.63.0"
|
|
44
|
+
"command-stream": "^1.6.2",
|
|
45
|
+
"playwright": "^1.63.0",
|
|
46
|
+
"selenium-webdriver": "^4.50.0"
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|
|
47
49
|
"@eslint/js": "^10.0.1",
|
package/src/automation.mjs
CHANGED
|
@@ -5,8 +5,11 @@ import {
|
|
|
5
5
|
attachViaExtension,
|
|
6
6
|
EXTENSION_DIRECTORY,
|
|
7
7
|
launchRealBrowser,
|
|
8
|
+
findBrowserSource,
|
|
8
9
|
} from "browser-commander";
|
|
9
10
|
|
|
11
|
+
import { connectWebDriverAutomation } from "./webdriver-automation.mjs";
|
|
12
|
+
|
|
10
13
|
import { snapshotBrowser } from "./browser-options.mjs";
|
|
11
14
|
import { defaultBrowserProfile, ensureProfileIgnored } from "./profile.mjs";
|
|
12
15
|
import { IMPORT_CHOICES, migrationFor } from "./sign-in-import.mjs";
|
|
@@ -28,7 +31,10 @@ export function relayExtensionDirectory() {
|
|
|
28
31
|
*/
|
|
29
32
|
export function launchOptions(browser, profile, verbose = false, domains = []) {
|
|
30
33
|
const options = {
|
|
31
|
-
engine:
|
|
34
|
+
engine:
|
|
35
|
+
findBrowserSource(browser.channel)?.controlProtocol === "cdp"
|
|
36
|
+
? "playwright"
|
|
37
|
+
: "selenium",
|
|
32
38
|
channel: browser.channel,
|
|
33
39
|
headless: false,
|
|
34
40
|
verbose,
|
|
@@ -75,25 +81,29 @@ export function extensionInstructions(directory) {
|
|
|
75
81
|
* Connects the browser that fills forms and returns a page with `goto`,
|
|
76
82
|
* `evaluate`, `close`, and, for a launched profile, `clearCookies(domains)`.
|
|
77
83
|
*/
|
|
78
|
-
export async function connectAutomation(
|
|
79
|
-
browser,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
domains = [],
|
|
83
|
-
}) {
|
|
84
|
+
export async function connectAutomation(
|
|
85
|
+
{ browser, profile, verbose = false, domains = [] },
|
|
86
|
+
dependencies = {},
|
|
87
|
+
) {
|
|
84
88
|
if (browser.attach?.mode === "extension") {
|
|
85
89
|
return connectExtension();
|
|
86
90
|
}
|
|
91
|
+
if (findBrowserSource(browser.channel)?.controlProtocol === "bidi") {
|
|
92
|
+
return connectWebDriverAutomation(
|
|
93
|
+
{ browser, profile, verbose, domains },
|
|
94
|
+
dependencies,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
87
97
|
if (!browser.attach) {
|
|
88
98
|
await ensureProfileIgnored(profile, { verbose });
|
|
89
99
|
}
|
|
90
|
-
const connection = await
|
|
91
|
-
|
|
92
|
-
);
|
|
100
|
+
const connection = await (
|
|
101
|
+
dependencies.launchRealBrowser ?? launchRealBrowser
|
|
102
|
+
)(launchOptions(browser, profile, verbose, domains));
|
|
93
103
|
return {
|
|
94
104
|
goto: (url) => connection.page.goto(url),
|
|
95
105
|
evaluate: (script) => connection.page.evaluate(script),
|
|
96
|
-
close: () => connection.
|
|
106
|
+
close: () => connection.close(),
|
|
97
107
|
async clearCookies(names) {
|
|
98
108
|
for (const name of names) {
|
|
99
109
|
const escaped = name.replaceAll(".", "\\.");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { constants, existsSync, realpathSync } from "node:fs";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
|
|
@@ -7,10 +8,10 @@ import { BROWSER_IDS, findBrowserSource } from "browser-commander";
|
|
|
7
8
|
/** Catalogue entries reached through Browser Commander's public exports. */
|
|
8
9
|
export const browserSources = () => BROWSER_IDS.map(findBrowserSource);
|
|
9
10
|
|
|
10
|
-
/**
|
|
11
|
+
/** Catalogue channels with a launch control protocol. */
|
|
11
12
|
export function launchChannels() {
|
|
12
13
|
return browserSources()
|
|
13
|
-
.filter((source) => source.controlProtocol
|
|
14
|
+
.filter((source) => source.controlProtocol)
|
|
14
15
|
.flatMap((source) => [source.id, ...(source.aliases ?? [])]);
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -121,13 +122,88 @@ export function installedDescription(options = {}) {
|
|
|
121
122
|
/** Reject unavailable engines before creating an automated profile. */
|
|
122
123
|
export function validateChannel(channel) {
|
|
123
124
|
const known = findBrowserSource(channel);
|
|
124
|
-
if (known?.controlProtocol
|
|
125
|
+
if (known?.controlProtocol) {
|
|
125
126
|
return channel;
|
|
126
127
|
}
|
|
127
128
|
const reason = known
|
|
128
|
-
? `${channel}
|
|
129
|
+
? `${channel} has no launch control protocol in browser-commander's catalogue${known.family === "safari" ? " (Safari setup: https://github.com/link-foundation/browser-commander/issues/126)" : ""}`
|
|
129
130
|
: `unknown --browser-channel '${channel}'`;
|
|
130
131
|
throw new Error(
|
|
131
132
|
`${reason}; choose from ${launchChannels().join(", ")}. ${installedDescription()}`,
|
|
132
133
|
);
|
|
133
134
|
}
|
|
135
|
+
|
|
136
|
+
/** Resolves the selected installed binary using catalogue paths and PATH. */
|
|
137
|
+
export async function resolveExecutable(
|
|
138
|
+
{ channel, executablePath },
|
|
139
|
+
discovery = {},
|
|
140
|
+
) {
|
|
141
|
+
const platform = discovery.platform ?? process.platform;
|
|
142
|
+
const environment = discovery.environment ?? process.env;
|
|
143
|
+
const options = {
|
|
144
|
+
...discovery,
|
|
145
|
+
platform,
|
|
146
|
+
environment:
|
|
147
|
+
platform === "win32"
|
|
148
|
+
? Object.fromEntries(
|
|
149
|
+
Object.entries(environment).map(([key, value]) => [
|
|
150
|
+
key.toUpperCase(),
|
|
151
|
+
value,
|
|
152
|
+
]),
|
|
153
|
+
)
|
|
154
|
+
: environment,
|
|
155
|
+
};
|
|
156
|
+
const source = findBrowserSource(channel);
|
|
157
|
+
const candidates = executablePath
|
|
158
|
+
? [executablePath]
|
|
159
|
+
: installedCandidates({ ...source, roots: {} }, options);
|
|
160
|
+
for (const candidate of candidates) {
|
|
161
|
+
try {
|
|
162
|
+
await (discovery.access ?? access)(candidate, constants.X_OK);
|
|
163
|
+
return candidate;
|
|
164
|
+
} catch {
|
|
165
|
+
// Continue through the catalogue's executable paths and PATH names.
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
throw new Error(
|
|
169
|
+
`could not find an installed ${channel} browser; use --browser-executable`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Refuses a source profile even when reached through a symlink. */
|
|
174
|
+
export function assertDedicatedProfile(profile, discovery = {}) {
|
|
175
|
+
const physical = (file) => {
|
|
176
|
+
try {
|
|
177
|
+
return realpathSync(file);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (error.code !== "ENOENT") {
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
return path.join(physical(path.dirname(file)), path.basename(file));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const requested = physical(path.resolve(profile));
|
|
186
|
+
const roots = browserSources().flatMap((source) =>
|
|
187
|
+
installedCandidates(
|
|
188
|
+
{
|
|
189
|
+
...source,
|
|
190
|
+
executables: {},
|
|
191
|
+
executableNames: [],
|
|
192
|
+
},
|
|
193
|
+
discovery,
|
|
194
|
+
),
|
|
195
|
+
);
|
|
196
|
+
for (const root of roots) {
|
|
197
|
+
const relative = path.relative(physical(path.resolve(root)), requested);
|
|
198
|
+
if (
|
|
199
|
+
relative === "" ||
|
|
200
|
+
(!relative.startsWith(`..${path.sep}`) &&
|
|
201
|
+
relative !== ".." &&
|
|
202
|
+
!path.isAbsolute(relative))
|
|
203
|
+
) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"automation requires a dedicated profile, not a browser default profile; use --browser-import to reuse sign-in",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
package/src/browser-options.mjs
CHANGED
|
@@ -55,7 +55,11 @@ export function parseBrowserOptions({
|
|
|
55
55
|
channel: attach === "extension" ? channel : validateChannel(channel),
|
|
56
56
|
executable: executable ? path.resolve(executable) : null,
|
|
57
57
|
import: importFrom ? parseImport(importFrom) : null,
|
|
58
|
-
importScope:
|
|
58
|
+
importScope:
|
|
59
|
+
(importScope === undefined || importScope === null) &&
|
|
60
|
+
findBrowserSource(channel)?.controlProtocol === "bidi"
|
|
61
|
+
? "domains"
|
|
62
|
+
: parseImportScope(importScope, importFrom),
|
|
59
63
|
attach: attach ? parseAttach(attach) : null,
|
|
60
64
|
preferences: parsePreferences(preferences),
|
|
61
65
|
restrictions: validateRestrictions(restrictions),
|
|
@@ -80,9 +84,34 @@ export function parseBrowserOptions({
|
|
|
80
84
|
"--browser-attach extension cannot be combined with --browser-executable, --browser-pref, or --browser-restriction",
|
|
81
85
|
);
|
|
82
86
|
}
|
|
87
|
+
validateEngineOptions(options);
|
|
83
88
|
return options;
|
|
84
89
|
}
|
|
85
90
|
|
|
91
|
+
/** Rejects capabilities the installed upstream non-CDP launchers lack. */
|
|
92
|
+
function validateEngineOptions(options) {
|
|
93
|
+
if (
|
|
94
|
+
options.attach?.mode === "extension" ||
|
|
95
|
+
findBrowserSource(options.channel)?.controlProtocol === "cdp"
|
|
96
|
+
) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const unsupported = options.attach
|
|
100
|
+
? "--browser-attach snapshot"
|
|
101
|
+
: options.restrictions.length
|
|
102
|
+
? "--browser-restriction"
|
|
103
|
+
: Object.keys(options.preferences).length
|
|
104
|
+
? "--browser-pref"
|
|
105
|
+
: options.import && options.importScope === "full"
|
|
106
|
+
? "--browser-import-scope full"
|
|
107
|
+
: null;
|
|
108
|
+
if (unsupported) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`${unsupported} is unavailable for ${options.channel} in the installed browser-commander; use --browser-import-scope domains for sign-in cookies`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
/** Parses `<browser>[:profile]`, `default`, or `auto`. */
|
|
87
116
|
export function parseImport(spec, discovery = {}) {
|
|
88
117
|
const separator = spec.indexOf(":");
|
package/src/cli.mjs
CHANGED
|
@@ -227,7 +227,8 @@ export async function main(args = process.argv.slice(2)) {
|
|
|
227
227
|
browser: values.browser,
|
|
228
228
|
browserOptions,
|
|
229
229
|
browserProfile: path.resolve(
|
|
230
|
-
values["browser-profile"] ??
|
|
230
|
+
values["browser-profile"] ??
|
|
231
|
+
defaultBrowserProfile({ channel: browserOptions.channel }),
|
|
231
232
|
),
|
|
232
233
|
verbose: values.verbose,
|
|
233
234
|
verifyRelease: values["verify-release"],
|
|
@@ -273,7 +274,10 @@ function browserSummary(command, values, browserOptions) {
|
|
|
273
274
|
channel: browserOptions.channel,
|
|
274
275
|
profile:
|
|
275
276
|
values.browser === "automated" && !browserOptions.attach
|
|
276
|
-
? path.resolve(
|
|
277
|
+
? path.resolve(
|
|
278
|
+
values["browser-profile"] ??
|
|
279
|
+
defaultBrowserProfile({ channel: browserOptions.channel }),
|
|
280
|
+
)
|
|
277
281
|
: undefined,
|
|
278
282
|
import: browserOptions.import ?? undefined,
|
|
279
283
|
attach: browserOptions.attach ?? undefined,
|
package/src/profile.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { exec } from "command-stream";
|
|
6
|
+
import { findBrowserSource } from "browser-commander";
|
|
6
7
|
|
|
7
8
|
const APPLICATION = "package-registry-manager";
|
|
8
9
|
const PROFILE = "browser-profile";
|
|
@@ -17,20 +18,26 @@ export function defaultBrowserProfile({
|
|
|
17
18
|
platform = process.platform,
|
|
18
19
|
env = process.env,
|
|
19
20
|
home = os.homedir(),
|
|
21
|
+
channel = "chrome",
|
|
20
22
|
} = {}) {
|
|
23
|
+
const source = findBrowserSource(channel);
|
|
24
|
+
const profile =
|
|
25
|
+
source?.controlProtocol && source.controlProtocol !== "cdp"
|
|
26
|
+
? `${PROFILE}-${source.id}`
|
|
27
|
+
: PROFILE;
|
|
21
28
|
const join = platform === "win32" ? path.win32.join : path.posix.join;
|
|
22
29
|
if (platform === "win32") {
|
|
23
30
|
const base = env.LOCALAPPDATA || join(home, "AppData", "Local");
|
|
24
|
-
return join(base, APPLICATION,
|
|
31
|
+
return join(base, APPLICATION, profile);
|
|
25
32
|
}
|
|
26
33
|
if (platform === "darwin") {
|
|
27
|
-
return join(home, "Library", "Application Support", APPLICATION,
|
|
34
|
+
return join(home, "Library", "Application Support", APPLICATION, profile);
|
|
28
35
|
}
|
|
29
36
|
// The XDG specification ignores relative values.
|
|
30
37
|
const state = path.posix.isAbsolute(env.XDG_STATE_HOME ?? "")
|
|
31
38
|
? env.XDG_STATE_HOME
|
|
32
39
|
: join(home, ".local", "state");
|
|
33
|
-
return join(state, APPLICATION,
|
|
40
|
+
return join(state, APPLICATION, profile);
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/** The profile location used before it moved out of the repository. */
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Firefox-family browsers use Browser Commander's native WebDriver/BiDi API.
|
|
2
|
+
// Its real/CDP launcher and migration target writers do not support them yet.
|
|
3
|
+
import { launchWebDriver, readBrowserCookies } from "browser-commander";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
assertDedicatedProfile,
|
|
7
|
+
resolveExecutable,
|
|
8
|
+
} from "./browser-catalogue.mjs";
|
|
9
|
+
import { ensureProfileIgnored } from "./profile.mjs";
|
|
10
|
+
import { IMPORT_CHOICES } from "./sign-in-import.mjs";
|
|
11
|
+
|
|
12
|
+
/** True only for a requested host or its subdomains, never a substring. */
|
|
13
|
+
export function matchesDomains(host, domains) {
|
|
14
|
+
const normalized = host.replace(/^\./, "").toLowerCase();
|
|
15
|
+
return domains.some(
|
|
16
|
+
(domain) => normalized === domain || normalized.endsWith(`.${domain}`),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The BiDi cookie shape; session cookies omit expiry. */
|
|
21
|
+
export function bidiCookie(cookie) {
|
|
22
|
+
const result = {
|
|
23
|
+
name: cookie.name,
|
|
24
|
+
value: { type: "string", value: cookie.value },
|
|
25
|
+
domain: cookie.domain,
|
|
26
|
+
path: cookie.path ?? "/",
|
|
27
|
+
httpOnly: cookie.httpOnly ?? false,
|
|
28
|
+
secure: cookie.secure ?? false,
|
|
29
|
+
sameSite: (cookie.sameSite ?? "Lax").toLowerCase(),
|
|
30
|
+
};
|
|
31
|
+
if (cookie.expires > 0) {
|
|
32
|
+
result.expiry = cookie.expires;
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Launches the selected installed Firefox variant and exposes the common page API. */
|
|
38
|
+
export async function connectWebDriverAutomation(
|
|
39
|
+
{ browser, profile, verbose, domains },
|
|
40
|
+
dependencies = {},
|
|
41
|
+
) {
|
|
42
|
+
// Validation happens before profile creation, including programmatic callers.
|
|
43
|
+
if (
|
|
44
|
+
browser.attach ||
|
|
45
|
+
browser.restrictions.length ||
|
|
46
|
+
Object.keys(browser.preferences).length ||
|
|
47
|
+
(browser.import && browser.importScope === "full")
|
|
48
|
+
) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`unsupported Firefox launch options for ${browser.channel}; use --browser-import-scope domains without snapshot, preferences or restrictions`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (browser.import && domains.length === 0) {
|
|
54
|
+
throw new Error("a domains import needs the registry's sign-in domains");
|
|
55
|
+
}
|
|
56
|
+
const executablePath = await (
|
|
57
|
+
dependencies.resolveExecutable ?? resolveExecutable
|
|
58
|
+
)({
|
|
59
|
+
channel: browser.channel.toLowerCase(),
|
|
60
|
+
executablePath: browser.executable,
|
|
61
|
+
});
|
|
62
|
+
assertDedicatedProfile(profile);
|
|
63
|
+
await ensureProfileIgnored(profile, { verbose });
|
|
64
|
+
const connection = await (dependencies.launchWebDriver ?? launchWebDriver)({
|
|
65
|
+
browser: "firefox",
|
|
66
|
+
executablePath,
|
|
67
|
+
userDataDir: profile,
|
|
68
|
+
headless: false,
|
|
69
|
+
bidi: true,
|
|
70
|
+
verbose,
|
|
71
|
+
});
|
|
72
|
+
try {
|
|
73
|
+
await connection.page.bidiCommand("storage.getCookies", {});
|
|
74
|
+
if (browser.import && !IMPORT_CHOICES.includes(browser.import.browser)) {
|
|
75
|
+
const read = dependencies.readCookies ?? readBrowserCookies;
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
for (const domain of domains) {
|
|
78
|
+
const cookies = await read({
|
|
79
|
+
browser: browser.import.browser,
|
|
80
|
+
profile: browser.import.profile ?? undefined,
|
|
81
|
+
domainFilter: domain,
|
|
82
|
+
cache: false,
|
|
83
|
+
});
|
|
84
|
+
for (const cookie of cookies) {
|
|
85
|
+
const key = JSON.stringify([cookie.domain, cookie.path, cookie.name]);
|
|
86
|
+
if (!matchesDomains(cookie.domain, domains) || seen.has(key)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
seen.add(key);
|
|
90
|
+
await connection.page.bidiCommand("storage.setCookie", {
|
|
91
|
+
cookie: bidiCookie(cookie),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await connection.close().catch(() => {});
|
|
98
|
+
throw new Error(
|
|
99
|
+
`could not prepare ${browser.channel} sign-in cookies (WebDriver BiDi storage is required)`,
|
|
100
|
+
{ cause: error },
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
goto: (url) => connection.page.goto(url),
|
|
105
|
+
evaluate: (script) => connection.page.evaluate(script),
|
|
106
|
+
close: () => connection.close(),
|
|
107
|
+
async clearCookies(names) {
|
|
108
|
+
const { cookies } = await connection.page.bidiCommand(
|
|
109
|
+
"storage.getCookies",
|
|
110
|
+
{},
|
|
111
|
+
);
|
|
112
|
+
for (const cookie of cookies) {
|
|
113
|
+
if (matchesDomains(cookie.domain, names)) {
|
|
114
|
+
await connection.page.bidiCommand("storage.deleteCookies", {
|
|
115
|
+
filter: {
|
|
116
|
+
name: cookie.name,
|
|
117
|
+
domain: cookie.domain,
|
|
118
|
+
path: cookie.path,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|