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.
@@ -0,0 +1,8 @@
1
+ # packages/playwright/lib/runner/session.js
2
+ # total: 25.9 KB
3
+
4
+ ## Inlined (1)
5
+ 24.2 KB packages/playwright/src/runner/session.ts
6
+
7
+ ## External (1)
8
+ playwright-core/lib/coreBundle
@@ -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 count = Math.max(1, Math.min(hint?.workers || 1, cpus));
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 KB
2
+ # total: 7.6 KB
3
3
 
4
4
  ## Inlined (1)
5
- 5.5 KB packages/playwright/src/runner/workerStandby.ts
5
+ 5.9 KB packages/playwright/src/runner/workerStandby.ts
6
6
 
7
- ## External (0)
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 playwright = require("playwright-core");
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
- return key ? readContextHints()[key] : void 0;
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 import_path5 = __toESM(require("path"));
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 { captureRawStack, stringifyStackFrames: stringifyStackFrames2, filteredStackTrace: filteredStackTrace3 } = require("playwright-core/lib/coreBundle").utils;
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 = filteredStackTrace3(captureRawStack()).slice(1);
1001
+ boxedStack = captureFilteredStackTrace().slice(1);
1000
1002
  stack ??= boxedStack;
1001
1003
  }
1002
- stack ??= filteredStackTrace3(captureRawStack());
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: filteredStackTrace4 } = require("playwright-core/lib/coreBundle").utils;
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: filteredStackTrace4,
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: filteredStackTrace4, ignoreSnapshots: false, updateSnapshots: "default" });
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 import_path4 = __toESM(require("path"));
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 playwright = require("playwright-core");
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 = import_path4.default.join(import_os.default.tmpdir(), "flashwright-context-hints.json");
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
- return key ? readContextHints()[key] : void 0;
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(import_path5.default.join(__dirname, "..", "..", "test.js"));
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: 76.4 KB
2
+ # total: 81.0 KB
3
3
 
4
4
  ## Inlined (9)
5
- 1.4 KB packages/playwright/src/speculativeBrowser.ts
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
- 21.3 KB packages/playwright/src/worker/workerMain.ts
13
- 0.7 KB packages/playwright/src/worker/workerProcessEntry.ts
12
+ 22.5 KB packages/playwright/src/worker/workerMain.ts
13
+ 1.0 KB packages/playwright/src/worker/workerProcessEntry.ts
14
14
 
15
- ## External (7)
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.1",
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.1"
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"