flashwright 1.64.1-flashwright.1 → 1.64.1-flashwright.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/cli.js +11 -13
- package/lib/cli/testActions.js +2 -1
- package/lib/codeCache.js +31 -10
- package/lib/codeCache.js.txt +2 -2
- package/lib/common/index.js +12 -0
- package/lib/common/index.js.txt +2 -2
- package/lib/index.js +3 -0
- package/lib/matchers/expect.js +3 -2
- package/lib/matchers/expect.js.txt +2 -2
- package/lib/runner/index.js +47 -32
- package/lib/runner/index.js.txt +5 -4
- package/lib/runner/session.js +748 -0
- package/lib/runner/session.js.txt +8 -0
- package/lib/runner/workerStandby.js +11 -2
- package/lib/runner/workerStandby.js.txt +4 -3
- package/lib/speculativeBrowser.js +110 -3
- package/lib/worker/workerProcessEntry.js +135 -13
- package/lib/worker/workerProcessEntry.js.txt +6 -5
- package/package.json +3 -2
|
@@ -36,6 +36,7 @@ __export(workerStandby_exports, {
|
|
|
36
36
|
prespawnWorkerForTestCommand: () => prespawnWorkerForTestCommand,
|
|
37
37
|
recordWorkerCount: () => recordWorkerCount,
|
|
38
38
|
recordWorkerRestart: () => recordWorkerRestart,
|
|
39
|
+
retargetStandbys: () => retargetStandbys,
|
|
39
40
|
takeStandby: () => takeStandby,
|
|
40
41
|
workerEntryScript: () => workerEntryScript,
|
|
41
42
|
workerEnv: () => workerEnv
|
|
@@ -46,6 +47,7 @@ var import_crypto = __toESM(require("crypto"));
|
|
|
46
47
|
var import_fs = __toESM(require("fs"));
|
|
47
48
|
var import_os = __toESM(require("os"));
|
|
48
49
|
var import_path = __toESM(require("path"));
|
|
50
|
+
var import_session = require("./session");
|
|
49
51
|
var standbys = [];
|
|
50
52
|
var enabled = false;
|
|
51
53
|
var hintKey;
|
|
@@ -78,7 +80,8 @@ function prespawnWorkerForTestCommand(argv) {
|
|
|
78
80
|
cpus = import_os.default.availableParallelism?.() ?? import_os.default.cpus().length;
|
|
79
81
|
const hint = readWorkerCountHints()[hintKey];
|
|
80
82
|
hadHint = !!hint;
|
|
81
|
-
const
|
|
83
|
+
const sessionBrowsers = Number(process.env.FLASHWRIGHT_SESSION_STANDBYS) || 0;
|
|
84
|
+
const count = Math.max(1, Math.min(Math.max(hint?.workers || 1, sessionBrowsers), cpus));
|
|
82
85
|
keepSpare = !!hint?.restarts && count < cpus;
|
|
83
86
|
for (let i = 0; i < count + (keepSpare ? 1 : 0); i++)
|
|
84
87
|
spawnStandby();
|
|
@@ -89,7 +92,7 @@ function prespawnWorkerForTestCommand(argv) {
|
|
|
89
92
|
}
|
|
90
93
|
function spawnStandby(waitsForNextPhase = false) {
|
|
91
94
|
const entryScript = workerEntryScript();
|
|
92
|
-
const env = { ...workerEnv({}), PLAYWRIGHT_TEST: "1", FLASHWRIGHT_SPECULATIVE_BROWSER: waitsForNextPhase ? "next-phase" : "1" };
|
|
95
|
+
const env = { ...workerEnv({}), PLAYWRIGHT_TEST: "1", FLASHWRIGHT_SPECULATIVE_BROWSER: waitsForNextPhase ? "next-phase" : "1", ...(0, import_session.sessionStandbyEnv)() };
|
|
93
96
|
const child = import_child_process.default.fork(entryScript, {
|
|
94
97
|
detached: false,
|
|
95
98
|
env,
|
|
@@ -97,6 +100,7 @@ function spawnStandby(waitsForNextPhase = false) {
|
|
|
97
100
|
});
|
|
98
101
|
child.on("error", () => {
|
|
99
102
|
});
|
|
103
|
+
(0, import_session.requestSessionBrowser)(child);
|
|
100
104
|
const ready = new Promise((resolve) => {
|
|
101
105
|
const onMessage = (message) => {
|
|
102
106
|
if (message?.method === "ready") {
|
|
@@ -128,6 +132,10 @@ function ensureStandbysForConfig(configuredWorkers) {
|
|
|
128
132
|
if (enabled && !hadHint)
|
|
129
133
|
ensureStandbys(Math.min(configuredWorkers, cpus));
|
|
130
134
|
}
|
|
135
|
+
function retargetStandbys(argv) {
|
|
136
|
+
if (enabled)
|
|
137
|
+
hintKey = workerCountHintKey(argv);
|
|
138
|
+
}
|
|
131
139
|
function recordWorkerCount(count) {
|
|
132
140
|
workerCount = Math.max(workerCount, count);
|
|
133
141
|
}
|
|
@@ -191,6 +199,7 @@ function writeWorkerCountHint() {
|
|
|
191
199
|
prespawnWorkerForTestCommand,
|
|
192
200
|
recordWorkerCount,
|
|
193
201
|
recordWorkerRestart,
|
|
202
|
+
retargetStandbys,
|
|
194
203
|
takeStandby,
|
|
195
204
|
workerEntryScript,
|
|
196
205
|
workerEnv
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# packages/playwright/lib/runner/workerStandby.js
|
|
2
|
-
# total: 7.
|
|
2
|
+
# total: 7.6 KB
|
|
3
3
|
|
|
4
4
|
## Inlined (1)
|
|
5
|
-
5.
|
|
5
|
+
5.9 KB packages/playwright/src/runner/workerStandby.ts
|
|
6
6
|
|
|
7
|
-
## External (
|
|
7
|
+
## External (1)
|
|
8
|
+
./session
|
|
@@ -28,9 +28,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var speculativeBrowser_exports = {};
|
|
30
30
|
__export(speculativeBrowser_exports, {
|
|
31
|
+
handBrowsersToSession: () => handBrowsersToSession,
|
|
31
32
|
launchSpeculativeBrowser: () => launchSpeculativeBrowser,
|
|
32
33
|
prepareSpeculativeContext: () => prepareSpeculativeContext,
|
|
33
34
|
rememberRecycledContext: () => rememberRecycledContext,
|
|
35
|
+
returnSpeculativeBrowser: () => returnSpeculativeBrowser,
|
|
34
36
|
takeSpeculativeBrowser: () => takeSpeculativeBrowser
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(speculativeBrowser_exports);
|
|
@@ -41,11 +43,100 @@ var import_util = require("util");
|
|
|
41
43
|
const speculativeOptions = { handleSIGINT: false, headless: true, ...process.env.FLASHWRIGHT_ISOLATION === "strict" ? {} : { recycleContexts: true } };
|
|
42
44
|
const speculativeKey = /* @__PURE__ */ Symbol.for("flashwright.speculativeBrowser");
|
|
43
45
|
function launchSpeculativeBrowser() {
|
|
44
|
-
const
|
|
45
|
-
const launch = playwright.chromium.launch(speculativeOptions).catch(() => void 0);
|
|
46
|
+
const offered = process.env.FLASHWRIGHT_SESSION_OFFER ? receiveSessionBrowser() : Promise.resolve();
|
|
47
|
+
const launch = offered.then(() => require("playwright-core").chromium.launch(speculativeOptions)).catch(() => void 0);
|
|
46
48
|
globalThis[speculativeKey] = launch;
|
|
47
49
|
return launch;
|
|
48
50
|
}
|
|
51
|
+
const kBrowserMessage = "__flashwright_browser__";
|
|
52
|
+
const offerKey = /* @__PURE__ */ Symbol.for("flashwright.browserOffer");
|
|
53
|
+
let offeredLastUsed = 0;
|
|
54
|
+
const kOfferTimeoutAfterInit = 500;
|
|
55
|
+
function receiveSessionBrowser() {
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
let answered = false;
|
|
58
|
+
let timer;
|
|
59
|
+
const answer = () => {
|
|
60
|
+
answered = true;
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
resolve();
|
|
63
|
+
};
|
|
64
|
+
const parts = {};
|
|
65
|
+
const onMessage = (message, handle) => {
|
|
66
|
+
if (message?.method === "__init__" && !answered)
|
|
67
|
+
timer = setTimeout(answer, kOfferTimeoutAfterInit);
|
|
68
|
+
if (message?.method !== kBrowserMessage || message.params.sent)
|
|
69
|
+
return;
|
|
70
|
+
if (message.params.none) {
|
|
71
|
+
process.off("message", onMessage);
|
|
72
|
+
answer();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
parts[message.params.part] = handle;
|
|
76
|
+
parts.metadata ??= message.params.metadata;
|
|
77
|
+
if (!parts.write || !parts.read || !parts.metadata)
|
|
78
|
+
return;
|
|
79
|
+
process.off("message", onMessage);
|
|
80
|
+
if (answered) {
|
|
81
|
+
void sendToRunner(parts);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
globalThis[offerKey] = parts;
|
|
85
|
+
offeredLastUsed = parts.metadata.lastUsed ?? 0;
|
|
86
|
+
process.once("exit", () => {
|
|
87
|
+
if (globalThis[offerKey] === parts)
|
|
88
|
+
killHandedOverBrowser(parts.metadata);
|
|
89
|
+
});
|
|
90
|
+
answer();
|
|
91
|
+
};
|
|
92
|
+
process.on("message", onMessage);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async function handBrowsersToSession(browser, ranTests) {
|
|
96
|
+
if (!process.env.FLASHWRIGHT_SESSION_HANDOFF || !process.send)
|
|
97
|
+
return false;
|
|
98
|
+
const offer = globalThis[offerKey];
|
|
99
|
+
if (offer) {
|
|
100
|
+
delete globalThis[offerKey];
|
|
101
|
+
await sendToRunner(offer);
|
|
102
|
+
}
|
|
103
|
+
if (!browser)
|
|
104
|
+
return false;
|
|
105
|
+
const serverBrowser = browser._connection.toImpl?.(browser);
|
|
106
|
+
const handoff = await serverBrowser?.handoffForSession?.().catch(() => void 0);
|
|
107
|
+
if (!handoff)
|
|
108
|
+
return false;
|
|
109
|
+
handoff.metadata.lastUsed = ranTests ? Date.now() : offeredLastUsed;
|
|
110
|
+
await sendToRunner(handoff);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
async function sendToRunner(browser) {
|
|
114
|
+
const id = `${process.pid}-${browser.metadata.pid}`;
|
|
115
|
+
const send = (params, handle) => new Promise((resolve) => {
|
|
116
|
+
if (!process.connected)
|
|
117
|
+
return resolve(false);
|
|
118
|
+
process.send({ method: kBrowserMessage, params }, handle, void 0, (error) => resolve(!error));
|
|
119
|
+
});
|
|
120
|
+
if (await send({ id, part: "write", metadata: browser.metadata }, browser.write) && await send({ id, part: "read" }, browser.read)) {
|
|
121
|
+
await send({ id, sent: true }, void 0);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
browser.write.destroy();
|
|
125
|
+
browser.read.destroy();
|
|
126
|
+
killHandedOverBrowser(browser.metadata);
|
|
127
|
+
}
|
|
128
|
+
function killHandedOverBrowser(metadata) {
|
|
129
|
+
try {
|
|
130
|
+
process.kill(-metadata.pid, "SIGKILL");
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
for (const directory of metadata.tempDirectories) {
|
|
134
|
+
try {
|
|
135
|
+
import_fs.default.rmSync(directory, { recursive: true, force: true, maxRetries: 5 });
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
49
140
|
function prepareSpeculativeContext() {
|
|
50
141
|
const launch = globalThis[speculativeKey];
|
|
51
142
|
const hint = speculativeOptions.recycleContexts ? readContextHint() : void 0;
|
|
@@ -61,6 +152,7 @@ function prepareSpeculativeContext() {
|
|
|
61
152
|
const contextHintFile = import_path.default.join(import_os.default.tmpdir(), "flashwright-context-hints.json");
|
|
62
153
|
const maxContextHints = 200;
|
|
63
154
|
let rememberedContextHint = false;
|
|
155
|
+
let hintReadAhead;
|
|
64
156
|
function readContextHints() {
|
|
65
157
|
try {
|
|
66
158
|
return JSON.parse(import_fs.default.readFileSync(contextHintFile, "utf8"));
|
|
@@ -70,7 +162,10 @@ function readContextHints() {
|
|
|
70
162
|
}
|
|
71
163
|
function readContextHint() {
|
|
72
164
|
const key = process.env.FLASHWRIGHT_CONTEXT_HINT_KEY;
|
|
73
|
-
|
|
165
|
+
if (!key)
|
|
166
|
+
return;
|
|
167
|
+
hintReadAhead = { key, hint: readContextHints()[key] };
|
|
168
|
+
return hintReadAhead.hint;
|
|
74
169
|
}
|
|
75
170
|
function rememberRecycledContext(browser) {
|
|
76
171
|
const key = process.env.FLASHWRIGHT_CONTEXT_HINT_KEY;
|
|
@@ -81,6 +176,9 @@ function rememberRecycledContext(browser) {
|
|
|
81
176
|
if (rememberedContextHint || !key || !Object.keys(params).length || mayHoldSecrets || !isPlainJSON(params))
|
|
82
177
|
return;
|
|
83
178
|
rememberedContextHint = true;
|
|
179
|
+
const hint = hintReadAhead?.key === key ? hintReadAhead.hint : void 0;
|
|
180
|
+
if (hint && JSON.stringify(hint.params) === JSON.stringify(params) && hint.storageState === storageState)
|
|
181
|
+
return;
|
|
84
182
|
try {
|
|
85
183
|
const hints = readContextHints();
|
|
86
184
|
if (JSON.stringify(hints[key]?.params) === JSON.stringify(params) && hints[key]?.storageState === storageState)
|
|
@@ -100,6 +198,13 @@ function isPlainJSON(value) {
|
|
|
100
198
|
return value.every(isPlainJSON);
|
|
101
199
|
return typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every(isPlainJSON);
|
|
102
200
|
}
|
|
201
|
+
async function returnSpeculativeBrowser() {
|
|
202
|
+
const launch = globalThis[speculativeKey];
|
|
203
|
+
if (!launch || !process.env.FLASHWRIGHT_SESSION_HANDOFF)
|
|
204
|
+
return;
|
|
205
|
+
delete globalThis[speculativeKey];
|
|
206
|
+
await handBrowsersToSession(await launch, false);
|
|
207
|
+
}
|
|
103
208
|
async function takeSpeculativeBrowser(browserName, options) {
|
|
104
209
|
const launch = globalThis[speculativeKey];
|
|
105
210
|
if (!launch)
|
|
@@ -121,8 +226,10 @@ async function takeSpeculativeBrowser(browserName, options) {
|
|
|
121
226
|
}
|
|
122
227
|
// Annotate the CommonJS export names for ESM import in node:
|
|
123
228
|
0 && (module.exports = {
|
|
229
|
+
handBrowsersToSession,
|
|
124
230
|
launchSpeculativeBrowser,
|
|
125
231
|
prepareSpeculativeContext,
|
|
126
232
|
rememberRecycledContext,
|
|
233
|
+
returnSpeculativeBrowser,
|
|
127
234
|
takeSpeculativeBrowser
|
|
128
235
|
});
|
|
@@ -24,12 +24,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
|
|
25
25
|
// packages/playwright/src/worker/workerProcessEntry.ts
|
|
26
26
|
var import_codeCache = require("../codeCache");
|
|
27
|
-
var
|
|
27
|
+
var import_path6 = __toESM(require("path"));
|
|
28
28
|
var import_common4 = require("../common");
|
|
29
29
|
|
|
30
30
|
// packages/playwright/src/worker/workerMain.ts
|
|
31
|
+
var import_path4 = __toESM(require("path"));
|
|
31
32
|
var import_common3 = require("../common");
|
|
32
33
|
var globals = __toESM(require("../globals"));
|
|
34
|
+
var import_package = require("../package");
|
|
33
35
|
var import_expect = require("../matchers/expect");
|
|
34
36
|
|
|
35
37
|
// packages/playwright/src/util.ts
|
|
@@ -837,7 +839,7 @@ function testInfoError(error) {
|
|
|
837
839
|
// packages/playwright/src/worker/testInfo.ts
|
|
838
840
|
var import_common2 = require("../common");
|
|
839
841
|
var { ManualPromise: ManualPromise4 } = require("playwright-core/lib/coreBundle").iso;
|
|
840
|
-
var {
|
|
842
|
+
var { captureFilteredStackTrace, stringifyStackFrames: stringifyStackFrames2 } = require("playwright-core/lib/coreBundle").utils;
|
|
841
843
|
var { escapeWithQuotes: escapeWithQuotes2 } = require("playwright-core/lib/coreBundle").iso;
|
|
842
844
|
var { monotonicTime: monotonicTime3 } = require("playwright-core/lib/coreBundle").iso;
|
|
843
845
|
var { createCallIdGenerator } = require("playwright-core/lib/coreBundle").iso;
|
|
@@ -996,10 +998,10 @@ var TestInfoImpl = class {
|
|
|
996
998
|
let boxedStack = parentStep?.boxedStack;
|
|
997
999
|
let stack = data.stack;
|
|
998
1000
|
if (!boxedStack && data.box) {
|
|
999
|
-
boxedStack =
|
|
1001
|
+
boxedStack = captureFilteredStackTrace().slice(1);
|
|
1000
1002
|
stack ??= boxedStack;
|
|
1001
1003
|
}
|
|
1002
|
-
stack ??=
|
|
1004
|
+
stack ??= captureFilteredStackTrace();
|
|
1003
1005
|
const step = {
|
|
1004
1006
|
...data,
|
|
1005
1007
|
stepId,
|
|
@@ -1352,7 +1354,7 @@ var colors2 = require("playwright-core/lib/utilsBundle").colors;
|
|
|
1352
1354
|
var { ManualPromise: ManualPromise5 } = require("playwright-core/lib/coreBundle").iso;
|
|
1353
1355
|
var { removeFolders } = require("playwright-core/lib/coreBundle").utils;
|
|
1354
1356
|
var { gracefullyCloseAll } = require("playwright-core/lib/coreBundle").utils;
|
|
1355
|
-
var { filteredStackTrace:
|
|
1357
|
+
var { filteredStackTrace: filteredStackTrace3 } = require("playwright-core/lib/coreBundle").utils;
|
|
1356
1358
|
var WorkerMain = class extends import_common3.ProcessRunner {
|
|
1357
1359
|
constructor(params) {
|
|
1358
1360
|
super();
|
|
@@ -1406,6 +1408,20 @@ var WorkerMain = class extends import_common3.ProcessRunner {
|
|
|
1406
1408
|
}
|
|
1407
1409
|
return this._runFinished;
|
|
1408
1410
|
}
|
|
1411
|
+
// Flashwright: in a warm session, the runner reports a run's outcome without waiting for its
|
|
1412
|
+
// workers to exit, when what a stopping worker has left to do cannot change it: no test is
|
|
1413
|
+
// running, no error is waiting to be reported, and only Playwright's own worker fixtures remain,
|
|
1414
|
+
// which hand the browser over to the session (see the runner's session.ts). A worker whose
|
|
1415
|
+
// process.exit was replaced may never exit, which the runner reports after a timeout.
|
|
1416
|
+
canDetachTeardown() {
|
|
1417
|
+
if (!process.env.FLASHWRIGHT_SESSION_HANDOFF || process.env.DEBUG || process.exit !== import_common3.originalProcessExit)
|
|
1418
|
+
return false;
|
|
1419
|
+
if (!this._runFinished.isDone() || this._currentTest || this._fatalErrors.length)
|
|
1420
|
+
return false;
|
|
1421
|
+
return [...this._fixtureRunner.instanceForId.values()].every(({ registration }) => {
|
|
1422
|
+
return registration.scope !== "worker" || typeof registration.fn !== "function" || registration.location.file.startsWith(import_package.packageRoot + import_path4.default.sep);
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1409
1425
|
async gracefullyClose() {
|
|
1410
1426
|
try {
|
|
1411
1427
|
await this._stop();
|
|
@@ -1522,6 +1538,8 @@ var WorkerMain = class extends import_common3.ProcessRunner {
|
|
|
1522
1538
|
await this._runTest(tests[i], entry.retry, tests[i + 1]);
|
|
1523
1539
|
debugTest(`test finished "${tests[i].title}"`);
|
|
1524
1540
|
}
|
|
1541
|
+
if (tests.length)
|
|
1542
|
+
globalThis[/* @__PURE__ */ Symbol.for("flashwright.codeCache")]?.ranTests();
|
|
1525
1543
|
} catch (e) {
|
|
1526
1544
|
this._fatalErrors.push(testInfoError(e));
|
|
1527
1545
|
void this._stop();
|
|
@@ -1599,7 +1617,7 @@ var WorkerMain = class extends import_common3.ProcessRunner {
|
|
|
1599
1617
|
globals.setCurrentTestInfo(testInfo);
|
|
1600
1618
|
(0, import_expect.setExpectConfig)({
|
|
1601
1619
|
testInfo,
|
|
1602
|
-
filteredStackTrace:
|
|
1620
|
+
filteredStackTrace: filteredStackTrace3,
|
|
1603
1621
|
ignoreSnapshots: testInfo._projectInternal.project.ignoreSnapshots,
|
|
1604
1622
|
updateSnapshots: testInfo.config.updateSnapshots,
|
|
1605
1623
|
timeout: testInfo._projectInternal.expect?.timeout,
|
|
@@ -1730,7 +1748,7 @@ var WorkerMain = class extends import_common3.ProcessRunner {
|
|
|
1730
1748
|
testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed + afterHooksSlot.elapsed | 0;
|
|
1731
1749
|
this._currentTest = null;
|
|
1732
1750
|
globals.setCurrentTestInfo(null);
|
|
1733
|
-
(0, import_expect.setExpectConfig)({ testInfo: null, filteredStackTrace:
|
|
1751
|
+
(0, import_expect.setExpectConfig)({ testInfo: null, filteredStackTrace: filteredStackTrace3, ignoreSnapshots: false, updateSnapshots: "default" });
|
|
1734
1752
|
this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
|
|
1735
1753
|
const preserveOutput = this._config.config.preserveOutput === "always" || this._config.config.preserveOutput === "failures-only" && testInfo._isFailure();
|
|
1736
1754
|
if (!preserveOutput)
|
|
@@ -1861,15 +1879,104 @@ var create = (params) => new WorkerMain(params);
|
|
|
1861
1879
|
// packages/playwright/src/speculativeBrowser.ts
|
|
1862
1880
|
var import_fs4 = __toESM(require("fs"));
|
|
1863
1881
|
var import_os = __toESM(require("os"));
|
|
1864
|
-
var
|
|
1882
|
+
var import_path5 = __toESM(require("path"));
|
|
1865
1883
|
var speculativeOptions = { handleSIGINT: false, headless: true, ...process.env.FLASHWRIGHT_ISOLATION === "strict" ? {} : { recycleContexts: true } };
|
|
1866
1884
|
var speculativeKey = /* @__PURE__ */ Symbol.for("flashwright.speculativeBrowser");
|
|
1867
1885
|
function launchSpeculativeBrowser() {
|
|
1868
|
-
const
|
|
1869
|
-
const launch = playwright.chromium.launch(speculativeOptions).catch(() => void 0);
|
|
1886
|
+
const offered = process.env.FLASHWRIGHT_SESSION_OFFER ? receiveSessionBrowser() : Promise.resolve();
|
|
1887
|
+
const launch = offered.then(() => require("playwright-core").chromium.launch(speculativeOptions)).catch(() => void 0);
|
|
1870
1888
|
globalThis[speculativeKey] = launch;
|
|
1871
1889
|
return launch;
|
|
1872
1890
|
}
|
|
1891
|
+
var kBrowserMessage = "__flashwright_browser__";
|
|
1892
|
+
var offerKey = /* @__PURE__ */ Symbol.for("flashwright.browserOffer");
|
|
1893
|
+
var offeredLastUsed = 0;
|
|
1894
|
+
var kOfferTimeoutAfterInit = 500;
|
|
1895
|
+
function receiveSessionBrowser() {
|
|
1896
|
+
return new Promise((resolve) => {
|
|
1897
|
+
let answered = false;
|
|
1898
|
+
let timer;
|
|
1899
|
+
const answer = () => {
|
|
1900
|
+
answered = true;
|
|
1901
|
+
clearTimeout(timer);
|
|
1902
|
+
resolve();
|
|
1903
|
+
};
|
|
1904
|
+
const parts = {};
|
|
1905
|
+
const onMessage = (message, handle) => {
|
|
1906
|
+
if (message?.method === "__init__" && !answered)
|
|
1907
|
+
timer = setTimeout(answer, kOfferTimeoutAfterInit);
|
|
1908
|
+
if (message?.method !== kBrowserMessage || message.params.sent)
|
|
1909
|
+
return;
|
|
1910
|
+
if (message.params.none) {
|
|
1911
|
+
process.off("message", onMessage);
|
|
1912
|
+
answer();
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
parts[message.params.part] = handle;
|
|
1916
|
+
parts.metadata ??= message.params.metadata;
|
|
1917
|
+
if (!parts.write || !parts.read || !parts.metadata)
|
|
1918
|
+
return;
|
|
1919
|
+
process.off("message", onMessage);
|
|
1920
|
+
if (answered) {
|
|
1921
|
+
void sendToRunner(parts);
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
globalThis[offerKey] = parts;
|
|
1925
|
+
offeredLastUsed = parts.metadata.lastUsed ?? 0;
|
|
1926
|
+
process.once("exit", () => {
|
|
1927
|
+
if (globalThis[offerKey] === parts)
|
|
1928
|
+
killHandedOverBrowser(parts.metadata);
|
|
1929
|
+
});
|
|
1930
|
+
answer();
|
|
1931
|
+
};
|
|
1932
|
+
process.on("message", onMessage);
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
async function handBrowsersToSession(browser, ranTests) {
|
|
1936
|
+
if (!process.env.FLASHWRIGHT_SESSION_HANDOFF || !process.send)
|
|
1937
|
+
return false;
|
|
1938
|
+
const offer = globalThis[offerKey];
|
|
1939
|
+
if (offer) {
|
|
1940
|
+
delete globalThis[offerKey];
|
|
1941
|
+
await sendToRunner(offer);
|
|
1942
|
+
}
|
|
1943
|
+
if (!browser)
|
|
1944
|
+
return false;
|
|
1945
|
+
const serverBrowser = browser._connection.toImpl?.(browser);
|
|
1946
|
+
const handoff = await serverBrowser?.handoffForSession?.().catch(() => void 0);
|
|
1947
|
+
if (!handoff)
|
|
1948
|
+
return false;
|
|
1949
|
+
handoff.metadata.lastUsed = ranTests ? Date.now() : offeredLastUsed;
|
|
1950
|
+
await sendToRunner(handoff);
|
|
1951
|
+
return true;
|
|
1952
|
+
}
|
|
1953
|
+
async function sendToRunner(browser) {
|
|
1954
|
+
const id = `${process.pid}-${browser.metadata.pid}`;
|
|
1955
|
+
const send = (params, handle) => new Promise((resolve) => {
|
|
1956
|
+
if (!process.connected)
|
|
1957
|
+
return resolve(false);
|
|
1958
|
+
process.send({ method: kBrowserMessage, params }, handle, void 0, (error) => resolve(!error));
|
|
1959
|
+
});
|
|
1960
|
+
if (await send({ id, part: "write", metadata: browser.metadata }, browser.write) && await send({ id, part: "read" }, browser.read)) {
|
|
1961
|
+
await send({ id, sent: true }, void 0);
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
browser.write.destroy();
|
|
1965
|
+
browser.read.destroy();
|
|
1966
|
+
killHandedOverBrowser(browser.metadata);
|
|
1967
|
+
}
|
|
1968
|
+
function killHandedOverBrowser(metadata) {
|
|
1969
|
+
try {
|
|
1970
|
+
process.kill(-metadata.pid, "SIGKILL");
|
|
1971
|
+
} catch {
|
|
1972
|
+
}
|
|
1973
|
+
for (const directory of metadata.tempDirectories) {
|
|
1974
|
+
try {
|
|
1975
|
+
import_fs4.default.rmSync(directory, { recursive: true, force: true, maxRetries: 5 });
|
|
1976
|
+
} catch {
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1873
1980
|
function prepareSpeculativeContext() {
|
|
1874
1981
|
const launch = globalThis[speculativeKey];
|
|
1875
1982
|
const hint = speculativeOptions.recycleContexts ? readContextHint() : void 0;
|
|
@@ -1882,7 +1989,8 @@ function prepareSpeculativeContext() {
|
|
|
1882
1989
|
return browser;
|
|
1883
1990
|
});
|
|
1884
1991
|
}
|
|
1885
|
-
var contextHintFile =
|
|
1992
|
+
var contextHintFile = import_path5.default.join(import_os.default.tmpdir(), "flashwright-context-hints.json");
|
|
1993
|
+
var hintReadAhead;
|
|
1886
1994
|
function readContextHints() {
|
|
1887
1995
|
try {
|
|
1888
1996
|
return JSON.parse(import_fs4.default.readFileSync(contextHintFile, "utf8"));
|
|
@@ -1892,7 +2000,17 @@ function readContextHints() {
|
|
|
1892
2000
|
}
|
|
1893
2001
|
function readContextHint() {
|
|
1894
2002
|
const key = process.env.FLASHWRIGHT_CONTEXT_HINT_KEY;
|
|
1895
|
-
|
|
2003
|
+
if (!key)
|
|
2004
|
+
return;
|
|
2005
|
+
hintReadAhead = { key, hint: readContextHints()[key] };
|
|
2006
|
+
return hintReadAhead.hint;
|
|
2007
|
+
}
|
|
2008
|
+
async function returnSpeculativeBrowser() {
|
|
2009
|
+
const launch = globalThis[speculativeKey];
|
|
2010
|
+
if (!launch || !process.env.FLASHWRIGHT_SESSION_HANDOFF)
|
|
2011
|
+
return;
|
|
2012
|
+
delete globalThis[speculativeKey];
|
|
2013
|
+
await handBrowsersToSession(await launch, false);
|
|
1896
2014
|
}
|
|
1897
2015
|
|
|
1898
2016
|
// packages/playwright/src/worker/workerProcessEntry.ts
|
|
@@ -1901,11 +2019,15 @@ if (process.env.FLASHWRIGHT_SPECULATIVE_BROWSER) {
|
|
|
1901
2019
|
let initialized = false;
|
|
1902
2020
|
process.on("message", (message) => initialized ||= message?.method === "__init__");
|
|
1903
2021
|
const idleTime = process.env.FLASHWRIGHT_SPECULATIVE_BROWSER === "next-phase" ? 0 : kSpareIdleTime;
|
|
2022
|
+
process.on("message", (message) => {
|
|
2023
|
+
if (message?.method === "__flashwright_return_browser__" && !initialized)
|
|
2024
|
+
void returnSpeculativeBrowser().finally(() => process.send?.({ method: "__flashwright_browser_returned__" }));
|
|
2025
|
+
});
|
|
1904
2026
|
void launchSpeculativeBrowser().then(() => setTimeout(() => {
|
|
1905
2027
|
if (initialized)
|
|
1906
2028
|
return;
|
|
1907
2029
|
try {
|
|
1908
|
-
require(
|
|
2030
|
+
require(import_path6.default.join(__dirname, "..", "..", "test.js"));
|
|
1909
2031
|
} catch {
|
|
1910
2032
|
}
|
|
1911
2033
|
if (!initialized)
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
# packages/playwright/lib/worker/workerProcessEntry.js
|
|
2
|
-
# total:
|
|
2
|
+
# total: 81.0 KB
|
|
3
3
|
|
|
4
4
|
## Inlined (9)
|
|
5
|
-
|
|
5
|
+
4.6 KB packages/playwright/src/speculativeBrowser.ts
|
|
6
6
|
3.7 KB packages/playwright/src/util.ts
|
|
7
7
|
8.7 KB packages/playwright/src/worker/fixtureRunner.ts
|
|
8
8
|
21.3 KB packages/playwright/src/worker/testInfo.ts
|
|
9
9
|
11.9 KB packages/playwright/src/worker/testTracing.ts
|
|
10
10
|
5.4 KB packages/playwright/src/worker/timeoutManager.ts
|
|
11
11
|
0.3 KB packages/playwright/src/worker/util.ts
|
|
12
|
-
|
|
13
|
-
0
|
|
12
|
+
22.5 KB packages/playwright/src/worker/workerMain.ts
|
|
13
|
+
1.0 KB packages/playwright/src/worker/workerProcessEntry.ts
|
|
14
14
|
|
|
15
|
-
## External (
|
|
15
|
+
## External (8)
|
|
16
16
|
../codeCache
|
|
17
17
|
../common
|
|
18
18
|
../globals
|
|
19
19
|
../matchers/expect
|
|
20
|
+
../package
|
|
20
21
|
playwright-core
|
|
21
22
|
playwright-core/lib/coreBundle
|
|
22
23
|
playwright-core/lib/utilsBundle
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flashwright",
|
|
3
|
-
"version": "1.64.1-flashwright.
|
|
3
|
+
"version": "1.64.1-flashwright.3",
|
|
4
4
|
"description": "Flashwright: a faster drop-in replacement for Playwright (fork of Playwright 1.64.1)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./lib/common": "./lib/common/index.js",
|
|
23
23
|
"./lib/program": "./lib/program.js",
|
|
24
24
|
"./lib/runner/workerStandby": "./lib/runner/workerStandby.js",
|
|
25
|
+
"./lib/runner/session": "./lib/runner/session.js",
|
|
25
26
|
"./lib/codeCache": "./lib/codeCache.js",
|
|
26
27
|
"./lib/runner": "./lib/runner/index.js",
|
|
27
28
|
"./lib/transform/babelBundle": "./lib/transform/babelBundle.js",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
},
|
|
48
49
|
"license": "Apache-2.0",
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"playwright-core": "npm:flashwright-core@1.64.1-flashwright.
|
|
51
|
+
"playwright-core": "npm:flashwright-core@1.64.1-flashwright.3"
|
|
51
52
|
},
|
|
52
53
|
"bugs": {
|
|
53
54
|
"url": "https://github.com/xcodebeast/flashwright/issues"
|