flashwright-core 1.64.1-flashwright.1 → 1.64.1-flashwright.2

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 CHANGED
@@ -13,4 +13,6 @@ npx playwright install chromium
13
13
 
14
14
  Without the test runner, `playwright` is `flashwright` and `playwright-core` is `flashwright-core`. See the [project page](https://github.com/xcodebeast/flashwright) for benchmarks, speed options and the differences from Playwright.
15
15
 
16
+ Re-running tests, like an agent checking its work? Outside CI, a warm session keeps the browser between runs, so one test re-runs in about 0.1 s instead of 0.7 s with Playwright ([warm session](https://github.com/xcodebeast/flashwright#warm-session); `FLASHWRIGHT_SESSION=0` turns it off).
17
+
16
18
  Flashwright is an independent fork and is not affiliated with Microsoft. Apache-2.0, like Playwright.
package/lib/coreBundle.js CHANGED
@@ -3043,6 +3043,7 @@ var init_httpServer = __esm({
3043
3043
  // packages/utils/processLauncher.ts
3044
3044
  var processLauncher_exports = {};
3045
3045
  __export(processLauncher_exports, {
3046
+ adoptProcess: () => adoptProcess,
3046
3047
  envArrayToObject: () => envArrayToObject,
3047
3048
  gracefullyCloseAll: () => gracefullyCloseAll,
3048
3049
  gracefullyCloseSet: () => gracefullyCloseSet,
@@ -3149,10 +3150,13 @@ async function launchProcess(options) {
3149
3150
  options.log(`[pid=${spawnedProcess.pid}][err] ` + data);
3150
3151
  });
3151
3152
  let processClosed = false;
3153
+ let released = false;
3152
3154
  let fulfillCleanup = () => {
3153
3155
  };
3154
3156
  const waitForCleanup = new Promise((f) => fulfillCleanup = f);
3155
3157
  spawnedProcess.once("close", (exitCode, signal) => {
3158
+ if (released)
3159
+ return;
3156
3160
  options.log(`[pid=${spawnedProcess.pid}] <process did exit: exitCode=${exitCode}, signal=${signal}>`);
3157
3161
  processClosed = true;
3158
3162
  gracefullyCloseSet.delete(gracefullyClose);
@@ -3219,7 +3223,103 @@ async function launchProcess(options) {
3219
3223
  killProcess();
3220
3224
  return waitForCleanup;
3221
3225
  }
3222
- return { launchedProcess: spawnedProcess, gracefullyClose, kill: killAndWait, killAndCleanup: killProcessAndCleanup };
3226
+ function release() {
3227
+ released = true;
3228
+ gracefullyCloseSet.delete(gracefullyClose);
3229
+ killSet.delete(killProcessAndCleanup);
3230
+ removeProcessHandlersIfNeeded();
3231
+ stdout.close();
3232
+ stderr.close();
3233
+ spawnedProcess.unref();
3234
+ options.log(`[pid=${spawnedProcess.pid}] <released>`);
3235
+ return { pid: spawnedProcess.pid, tempDirectories: options.tempDirectories };
3236
+ }
3237
+ return { launchedProcess: spawnedProcess, gracefullyClose, kill: killAndWait, killAndCleanup: killProcessAndCleanup, release };
3238
+ }
3239
+ function adoptProcess(options) {
3240
+ const { pid } = options;
3241
+ let processClosed = false;
3242
+ let released = false;
3243
+ let fulfillCleanup = () => {
3244
+ };
3245
+ const waitForCleanup = new Promise((f) => fulfillCleanup = f);
3246
+ options.log(`<adopted> pid=${pid}`);
3247
+ void options.exited.then(async () => {
3248
+ if (released)
3249
+ return;
3250
+ options.log(`[pid=${pid}] <process did exit>`);
3251
+ processClosed = true;
3252
+ gracefullyCloseSet.delete(gracefullyClose);
3253
+ killSet.delete(killProcessAndCleanup);
3254
+ removeProcessHandlersIfNeeded();
3255
+ options.onExit();
3256
+ const errors = await removeFolders(options.tempDirectories);
3257
+ for (let i = 0; i < options.tempDirectories.length; ++i) {
3258
+ if (errors[i])
3259
+ options.log(`[pid=${pid}] exception while removing ${options.tempDirectories[i]}: ${errors[i]}`);
3260
+ }
3261
+ fulfillCleanup();
3262
+ });
3263
+ addProcessHandlerIfNeeded("exit");
3264
+ if (options.handleSIGINT)
3265
+ addProcessHandlerIfNeeded("SIGINT");
3266
+ if (options.handleSIGTERM)
3267
+ addProcessHandlerIfNeeded("SIGTERM");
3268
+ if (options.handleSIGHUP)
3269
+ addProcessHandlerIfNeeded("SIGHUP");
3270
+ gracefullyCloseSet.add(gracefullyClose);
3271
+ killSet.add(killProcessAndCleanup);
3272
+ let gracefullyClosing = false;
3273
+ async function gracefullyClose() {
3274
+ if (gracefullyClosing) {
3275
+ killProcess();
3276
+ await waitForCleanup;
3277
+ return;
3278
+ }
3279
+ gracefullyClosing = true;
3280
+ await options.attemptToGracefullyClose().catch(() => killProcess());
3281
+ await waitForCleanup;
3282
+ }
3283
+ function killProcess() {
3284
+ gracefullyCloseSet.delete(gracefullyClose);
3285
+ killSet.delete(killProcessAndCleanup);
3286
+ removeProcessHandlersIfNeeded();
3287
+ if (processClosed)
3288
+ return;
3289
+ options.log(`[pid=${pid}] <will force kill>`);
3290
+ try {
3291
+ process.kill(-pid, "SIGKILL");
3292
+ } catch (e) {
3293
+ options.log(`[pid=${pid}] exception while trying to kill process: ${e}`);
3294
+ }
3295
+ }
3296
+ function killProcessAndCleanup() {
3297
+ killProcess();
3298
+ for (const dir of options.tempDirectories) {
3299
+ try {
3300
+ import_fs6.default.rmSync(dir, { force: true, recursive: true, maxRetries: 5 });
3301
+ } catch (e) {
3302
+ options.log(`[pid=${pid}] exception while removing ${dir}: ${e}`);
3303
+ }
3304
+ }
3305
+ }
3306
+ function release() {
3307
+ released = true;
3308
+ gracefullyCloseSet.delete(gracefullyClose);
3309
+ killSet.delete(killProcessAndCleanup);
3310
+ removeProcessHandlersIfNeeded();
3311
+ options.log(`[pid=${pid}] <released>`);
3312
+ return { pid, tempDirectories: options.tempDirectories };
3313
+ }
3314
+ return {
3315
+ gracefullyClose,
3316
+ kill: () => {
3317
+ killProcess();
3318
+ return waitForCleanup;
3319
+ },
3320
+ killAndCleanup: killProcessAndCleanup,
3321
+ release
3322
+ };
3223
3323
  }
3224
3324
  function killProcessTree(pid) {
3225
3325
  try {
@@ -33792,6 +33892,85 @@ var init_chromiumSwitches = __esm({
33792
33892
  }
33793
33893
  });
33794
33894
 
33895
+ // packages/playwright-core/src/server/pipeTransport.ts
33896
+ var PipeTransport2;
33897
+ var init_pipeTransport2 = __esm({
33898
+ "packages/playwright-core/src/server/pipeTransport.ts"() {
33899
+ "use strict";
33900
+ init_debugLogger();
33901
+ PipeTransport2 = class {
33902
+ constructor(pipeWrite, pipeRead) {
33903
+ this._pendingBuffers = [];
33904
+ this._closed = false;
33905
+ this._pipeRead = pipeRead;
33906
+ this._pipeWrite = pipeWrite;
33907
+ pipeRead.on("data", (buffer) => this._dispatch(buffer));
33908
+ pipeRead.on("close", () => {
33909
+ this._closed = true;
33910
+ if (this._onclose)
33911
+ this._onclose.call(null);
33912
+ });
33913
+ pipeRead.on("error", (e) => debugLogger.log("error", e));
33914
+ pipeWrite.on("error", (e) => debugLogger.log("error", e));
33915
+ this.onmessage = void 0;
33916
+ }
33917
+ get onclose() {
33918
+ return this._onclose;
33919
+ }
33920
+ set onclose(onclose) {
33921
+ this._onclose = onclose;
33922
+ if (onclose && !this._pipeRead.readable)
33923
+ onclose();
33924
+ }
33925
+ send(message) {
33926
+ if (this._closed)
33927
+ throw new Error("Pipe has been closed");
33928
+ this._pipeWrite.write(JSON.stringify(message) + "\0");
33929
+ }
33930
+ close() {
33931
+ throw new Error("unimplemented");
33932
+ }
33933
+ // Flashwright: stops reading and gives the pipes to another owner, who continues the stream of
33934
+ // messages (see browserHandoff.ts). Only possible between two messages: returns undefined when
33935
+ // part of one has already been read.
33936
+ release() {
33937
+ if (this._closed || this._pendingBuffers.some((buffer) => buffer.length))
33938
+ return;
33939
+ this._closed = true;
33940
+ this._pipeRead.removeAllListeners("data");
33941
+ this._pipeRead.removeAllListeners("close");
33942
+ this._pipeRead.pause();
33943
+ return { write: this._pipeWrite, read: this._pipeRead };
33944
+ }
33945
+ _dispatch(buffer) {
33946
+ let end = buffer.indexOf("\0");
33947
+ if (end === -1) {
33948
+ this._pendingBuffers.push(buffer);
33949
+ return;
33950
+ }
33951
+ this._pendingBuffers.push(buffer.slice(0, end));
33952
+ const message = Buffer.concat(this._pendingBuffers).toString();
33953
+ setImmediate(() => {
33954
+ if (this.onmessage)
33955
+ this.onmessage.call(null, JSON.parse(message));
33956
+ });
33957
+ let start3 = end + 1;
33958
+ end = buffer.indexOf("\0", start3);
33959
+ while (end !== -1) {
33960
+ const message2 = buffer.toString(void 0, start3, end);
33961
+ setImmediate(() => {
33962
+ if (this.onmessage)
33963
+ this.onmessage.call(null, JSON.parse(message2));
33964
+ });
33965
+ start3 = end + 1;
33966
+ end = buffer.indexOf("\0", start3);
33967
+ }
33968
+ this._pendingBuffers = [buffer.slice(start3)];
33969
+ }
33970
+ };
33971
+ }
33972
+ });
33973
+
33795
33974
  // packages/playwright-core/src/server/chromium/crConnection.ts
33796
33975
  var ConnectionEvents, kBrowserCloseMessageId, CRConnection, CRSession, CDPSession;
33797
33976
  var init_crConnection = __esm({
@@ -33852,6 +34031,9 @@ var init_crConnection = __esm({
33852
34031
  if (!this._closed)
33853
34032
  this._transport.close();
33854
34033
  }
34034
+ transport() {
34035
+ return this._transport;
34036
+ }
33855
34037
  async createBrowserSession() {
33856
34038
  const { sessionId } = await this.rootSession.send("Target.attachToBrowserTarget");
33857
34039
  return new CDPSession(this.rootSession, sessionId);
@@ -37355,6 +37537,7 @@ var init_crBrowser = __esm({
37355
37537
  init_frames();
37356
37538
  init_network2();
37357
37539
  init_page();
37540
+ init_pipeTransport2();
37358
37541
  init_crConnection();
37359
37542
  init_crPage();
37360
37543
  init_crProtocolHelper();
@@ -37367,6 +37550,7 @@ var init_crBrowser = __esm({
37367
37550
  this._crPages = /* @__PURE__ */ new Map();
37368
37551
  this._serviceWorkers = /* @__PURE__ */ new Map();
37369
37552
  this._isConnecting = false;
37553
+ this._handingOff = false;
37370
37554
  this._version = "";
37371
37555
  this._majorVersion = 0;
37372
37556
  this._revision = "";
@@ -37400,7 +37584,16 @@ var init_crBrowser = __esm({
37400
37584
  browser._userAgent = version3.userAgent;
37401
37585
  browser.options.headful = !version3.userAgent.includes("Headless");
37402
37586
  if (!options.persistent) {
37587
+ const adopted = options.adoptContext && await browser.adoptHandedOverContext(options.adoptContext).then(() => true, () => false);
37588
+ if (options.adoptContext && !adopted)
37589
+ await session.send("Target.disposeBrowserContext", { browserContextId: options.adoptContext.id }).catch(() => {
37590
+ });
37403
37591
  await session.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true });
37592
+ if (adopted) {
37593
+ await session.send("Target.getTargetInfo");
37594
+ await browser._waitForAllPagesToBeInitialized();
37595
+ browser.startAdoptedContextRecycle();
37596
+ }
37404
37597
  return browser;
37405
37598
  }
37406
37599
  browser._defaultContext = new CRBrowserContext(browser, void 0, options.persistent);
@@ -37437,6 +37630,29 @@ var init_crBrowser = __esm({
37437
37630
  this._contexts.set(browserContextId, context);
37438
37631
  return context;
37439
37632
  }
37633
+ async doAdoptContext(browserContextId, options) {
37634
+ const context = new CRBrowserContext(this, browserContextId, options);
37635
+ await context.initialize();
37636
+ this._contexts.set(browserContextId, context);
37637
+ return context;
37638
+ }
37639
+ // Flashwright: leaves the browser as a new DevTools client would find it, attached to nothing,
37640
+ // and gives up its pipe (see Browser.handoffForSession).
37641
+ async doDetachForHandoff() {
37642
+ const transport = this._connection.transport();
37643
+ if (!(transport instanceof PipeTransport2))
37644
+ return;
37645
+ this._handingOff = true;
37646
+ const sessionIds = [...this._connection._sessions.keys()].filter((sessionId) => !!sessionId);
37647
+ const stopAttaching = this._session.send("Target.setAutoAttach", { autoAttach: false, waitForDebuggerOnStart: false, flatten: true });
37648
+ const detached = sessionIds.map((sessionId) => this._session.send("Target.detachFromTarget", { sessionId }).catch(() => {
37649
+ }));
37650
+ const lastReply = this._session.send("Browser.getVersion");
37651
+ await Promise.all([stopAttaching, ...detached, lastReply]);
37652
+ const pipes = transport.release();
37653
+ this._connection._onClose("Browser handed over");
37654
+ return pipes;
37655
+ }
37440
37656
  contexts() {
37441
37657
  return Array.from(this._contexts.values());
37442
37658
  }
@@ -37499,6 +37715,8 @@ var init_crBrowser = __esm({
37499
37715
  });
37500
37716
  }
37501
37717
  _onDetachedFromTarget(payload) {
37718
+ if (this._handingOff)
37719
+ return;
37502
37720
  const targetId = payload.targetId;
37503
37721
  const crPage = this._crPages.get(targetId);
37504
37722
  if (crPage) {
@@ -38453,70 +38671,25 @@ var init_backendAdb = __esm({
38453
38671
  }
38454
38672
  });
38455
38673
 
38456
- // packages/playwright-core/src/server/pipeTransport.ts
38457
- var PipeTransport2;
38458
- var init_pipeTransport2 = __esm({
38459
- "packages/playwright-core/src/server/pipeTransport.ts"() {
38674
+ // packages/playwright-core/src/server/browserHandoff.ts
38675
+ function browserLaunchKey(browserName, options) {
38676
+ const entries = Object.entries(options).filter(([key, value2]) => value2 !== void 0 && !kIgnoredLaunchOptions.has(key) && !key.startsWith("__testHook"));
38677
+ entries.sort(([a], [b]) => a < b ? -1 : 1);
38678
+ return JSON.stringify([browserName, entries]);
38679
+ }
38680
+ function takeBrowserOffer(browserName, options) {
38681
+ const offer = globalThis[offerKey];
38682
+ if (!offer || offer.metadata.browserName !== browserName || offer.metadata.launchKey !== browserLaunchKey(browserName, options))
38683
+ return;
38684
+ delete globalThis[offerKey];
38685
+ return offer;
38686
+ }
38687
+ var offerKey, kIgnoredLaunchOptions;
38688
+ var init_browserHandoff = __esm({
38689
+ "packages/playwright-core/src/server/browserHandoff.ts"() {
38460
38690
  "use strict";
38461
- init_debugLogger();
38462
- PipeTransport2 = class {
38463
- constructor(pipeWrite, pipeRead) {
38464
- this._pendingBuffers = [];
38465
- this._closed = false;
38466
- this._pipeRead = pipeRead;
38467
- this._pipeWrite = pipeWrite;
38468
- pipeRead.on("data", (buffer) => this._dispatch(buffer));
38469
- pipeRead.on("close", () => {
38470
- this._closed = true;
38471
- if (this._onclose)
38472
- this._onclose.call(null);
38473
- });
38474
- pipeRead.on("error", (e) => debugLogger.log("error", e));
38475
- pipeWrite.on("error", (e) => debugLogger.log("error", e));
38476
- this.onmessage = void 0;
38477
- }
38478
- get onclose() {
38479
- return this._onclose;
38480
- }
38481
- set onclose(onclose) {
38482
- this._onclose = onclose;
38483
- if (onclose && !this._pipeRead.readable)
38484
- onclose();
38485
- }
38486
- send(message) {
38487
- if (this._closed)
38488
- throw new Error("Pipe has been closed");
38489
- this._pipeWrite.write(JSON.stringify(message) + "\0");
38490
- }
38491
- close() {
38492
- throw new Error("unimplemented");
38493
- }
38494
- _dispatch(buffer) {
38495
- let end = buffer.indexOf("\0");
38496
- if (end === -1) {
38497
- this._pendingBuffers.push(buffer);
38498
- return;
38499
- }
38500
- this._pendingBuffers.push(buffer.slice(0, end));
38501
- const message = Buffer.concat(this._pendingBuffers).toString();
38502
- setImmediate(() => {
38503
- if (this.onmessage)
38504
- this.onmessage.call(null, JSON.parse(message));
38505
- });
38506
- let start3 = end + 1;
38507
- end = buffer.indexOf("\0", start3);
38508
- while (end !== -1) {
38509
- const message2 = buffer.toString(void 0, start3, end);
38510
- setImmediate(() => {
38511
- if (this.onmessage)
38512
- this.onmessage.call(null, JSON.parse(message2));
38513
- });
38514
- start3 = end + 1;
38515
- end = buffer.indexOf("\0", start3);
38516
- }
38517
- this._pendingBuffers = [buffer.slice(start3)];
38518
- }
38519
- };
38691
+ offerKey = /* @__PURE__ */ Symbol.for("flashwright.browserOffer");
38692
+ kIgnoredLaunchOptions = /* @__PURE__ */ new Set(["artifactsDir", "tracesDir", "downloadsPath", "timeout", "slowMo", "handleSIGINT", "handleSIGTERM", "handleSIGHUP"]);
38520
38693
  }
38521
38694
  });
38522
38695
 
@@ -38687,6 +38860,20 @@ function copyTestHooks(from, to) {
38687
38860
  to[key] = value2;
38688
38861
  }
38689
38862
  }
38863
+ async function closeOrKillProcess(gracefullyClose, kill, timeout) {
38864
+ let timer;
38865
+ try {
38866
+ await Promise.race([
38867
+ gracefullyClose(),
38868
+ new Promise((resolve, reject) => timer = setTimeout(reject, timeout))
38869
+ ]);
38870
+ } catch (ignored) {
38871
+ await kill().catch((ignored2) => {
38872
+ });
38873
+ } finally {
38874
+ clearTimeout(timer);
38875
+ }
38876
+ }
38690
38877
  var import_fs22, import_os10, import_path21, kNoXServerRunningError, BrowserType;
38691
38878
  var init_browserType = __esm({
38692
38879
  "packages/playwright-core/src/server/browserType.ts"() {
@@ -38702,6 +38889,7 @@ var init_browserType = __esm({
38702
38889
  init_processLauncher();
38703
38890
  init_debugLogger();
38704
38891
  init_browserContext();
38892
+ init_browserHandoff();
38705
38893
  init_helper();
38706
38894
  init_instrumentation();
38707
38895
  init_pipeTransport2();
@@ -38768,7 +38956,8 @@ var init_browserType = __esm({
38768
38956
  async _innerLaunch(progress2, options, persistent, protocolLogger, maybeUserDataDir) {
38769
38957
  options.proxy = options.proxy ? normalizeProxySettings(options.proxy) : void 0;
38770
38958
  const browserLogsCollector = new RecentLogsCollector();
38771
- const { browserProcess, userDataDir, artifactsDir, transport, wsEndpoint } = await this._launchProcess(progress2, options, !!persistent, browserLogsCollector, maybeUserDataDir);
38959
+ const offer = persistent ? void 0 : takeBrowserOffer(this._name, options);
38960
+ const { browserProcess, userDataDir, artifactsDir, transport, wsEndpoint } = offer ? await this._adoptProcess(progress2, offer, options, browserLogsCollector) : await this._launchProcess(progress2, options, !!persistent, browserLogsCollector, maybeUserDataDir);
38772
38961
  try {
38773
38962
  if (options.__testHookBeforeCreateBrowser)
38774
38963
  await progress2.race(options.__testHookBeforeCreateBrowser());
@@ -38789,7 +38978,9 @@ var init_browserType = __esm({
38789
38978
  browserLogsCollector,
38790
38979
  wsEndpoint,
38791
38980
  originalLaunchOptions: options,
38792
- userDataDir: persistent ? userDataDir : void 0
38981
+ userDataDir: persistent ? userDataDir : void 0,
38982
+ launchKey: browserLaunchKey(this._name, options),
38983
+ adoptContext: offer?.metadata.context
38793
38984
  };
38794
38985
  if (persistent)
38795
38986
  validateBrowserContextOptions(persistent, browserOptions);
@@ -38859,7 +39050,7 @@ var init_browserType = __esm({
38859
39050
  let transport = void 0;
38860
39051
  let browserProcess = void 0;
38861
39052
  const exitPromise = new ManualPromise();
38862
- const { launchedProcess, gracefullyClose, kill, killAndCleanup } = await progress2.race(launchProcess({
39053
+ const { launchedProcess, gracefullyClose, kill, killAndCleanup, release } = await progress2.race(launchProcess({
38863
39054
  command: prepared.executable,
38864
39055
  args: prepared.browserArguments,
38865
39056
  env: this.amendEnvironment(env, prepared.userDataDir, isPersistent, options),
@@ -38887,26 +39078,14 @@ var init_browserType = __esm({
38887
39078
  browserProcess.onclose(exitCode, signal);
38888
39079
  }
38889
39080
  }));
38890
- async function closeOrKill(timeout) {
38891
- let timer;
38892
- try {
38893
- await Promise.race([
38894
- gracefullyClose(),
38895
- new Promise((resolve, reject) => timer = setTimeout(reject, timeout))
38896
- ]);
38897
- } catch (ignored) {
38898
- await kill().catch((ignored2) => {
38899
- });
38900
- } finally {
38901
- clearTimeout(timer);
38902
- }
38903
- }
39081
+ const closeOrKill = (timeout) => closeOrKillProcess(gracefullyClose, kill, timeout);
38904
39082
  browserProcess = {
38905
39083
  onclose: void 0,
38906
39084
  process: launchedProcess,
38907
39085
  close: () => closeOrKill(options.__testHookBrowserCloseTimeout || DEFAULT_PLAYWRIGHT_TIMEOUT),
38908
39086
  kill,
38909
- killAndCleanup
39087
+ killAndCleanup,
39088
+ release
38910
39089
  };
38911
39090
  try {
38912
39091
  const { wsEndpoint } = await progress2.race([
@@ -38933,6 +39112,46 @@ ${updatedLog}`);
38933
39112
  throw error;
38934
39113
  }
38935
39114
  }
39115
+ // Flashwright: takes over a running browser that another process launched with the same options
39116
+ // and handed over (see browserHandoff.ts), instead of launching one.
39117
+ async _adoptProcess(progress2, offer, options, browserLogsCollector) {
39118
+ const { metadata } = offer;
39119
+ const tempDirectories = [...metadata.tempDirectories];
39120
+ let artifactsDir = options.artifactsDir;
39121
+ if (!artifactsDir) {
39122
+ artifactsDir = await progress2.race(import_fs22.default.promises.mkdtemp(import_path21.default.join(import_os10.default.tmpdir(), "playwright-artifacts-")));
39123
+ tempDirectories.push(artifactsDir);
39124
+ }
39125
+ const transport = new PipeTransport2(offer.write, offer.read);
39126
+ const exited = new Promise((resolve) => {
39127
+ if (!offer.read.readable)
39128
+ resolve();
39129
+ offer.read.once("close", () => resolve());
39130
+ });
39131
+ let browserProcess = void 0;
39132
+ const { gracefullyClose, kill, killAndCleanup, release } = adoptProcess({
39133
+ pid: metadata.pid,
39134
+ tempDirectories,
39135
+ exited,
39136
+ handleSIGINT: options.handleSIGINT ?? true,
39137
+ handleSIGTERM: options.handleSIGTERM ?? true,
39138
+ handleSIGHUP: options.handleSIGHUP ?? true,
39139
+ attemptToGracefullyClose: async () => this.attemptToGracefullyCloseBrowser(transport),
39140
+ onExit: () => browserProcess?.onclose?.(0, null),
39141
+ log: (message) => {
39142
+ progress2.log(message);
39143
+ browserLogsCollector.log(message);
39144
+ }
39145
+ });
39146
+ browserProcess = {
39147
+ onclose: void 0,
39148
+ close: () => closeOrKillProcess(gracefullyClose, kill, options.__testHookBrowserCloseTimeout || DEFAULT_PLAYWRIGHT_TIMEOUT),
39149
+ kill,
39150
+ killAndCleanup,
39151
+ release
39152
+ };
39153
+ return { browserProcess, artifactsDir, userDataDir: "", transport };
39154
+ }
38936
39155
  async connectOverCDP(progress2, params2) {
38937
39156
  throw new Error("CDP connections are only supported by Chromium");
38938
39157
  }
@@ -50598,6 +50817,13 @@ function raceWithTimeout(promise, timeout, timeoutValue) {
50598
50817
  const timedOut = new Promise((f) => timer = setTimeout(() => f(timeoutValue), timeout));
50599
50818
  return Promise.race([promise, timedOut]).finally(() => clearTimeout(timer));
50600
50819
  }
50820
+ function isPlainJSON(value2) {
50821
+ if (value2 === null || ["string", "number", "boolean", "undefined"].includes(typeof value2))
50822
+ return true;
50823
+ if (Array.isArray(value2))
50824
+ return value2.every(isPlainJSON);
50825
+ return typeof value2 === "object" && Object.getPrototypeOf(value2) === Object.prototype && Object.values(value2).every(isPlainJSON);
50826
+ }
50601
50827
  var import_fs28, BrowserContextEvent, BrowserContext, kRecycledPageTimeout, kRecycleTimeout, paramsThatAllowContextReuse, defaultNewContextParamValues;
50602
50828
  var init_browserContext = __esm({
50603
50829
  "packages/playwright-core/src/server/browserContext.ts"() {
@@ -50756,6 +50982,32 @@ if (self.ServiceWorkerContainer) ServiceWorkerContainer.prototype.register = asy
50756
50982
  this._pendingRecycle.catch(() => {
50757
50983
  });
50758
50984
  }
50985
+ // Flashwright: the context of a browser handed over by another process (see browserHandoff.ts)
50986
+ // clears the storage of the origins the previous process's tests used on its next reset.
50987
+ addVisitedOrigins(origins) {
50988
+ for (const origin of origins)
50989
+ this._origins.add(origin);
50990
+ }
50991
+ // Flashwright: called before the browser is handed over to another process. Waits for the reset
50992
+ // that started when the last test ended and describes the context for the process that adopts it,
50993
+ // or returns undefined when it cannot move: it holds state that lives in this process (a client
50994
+ // certificate proxy, a HAR recording, a video) or that a reset cannot clear.
50995
+ async prepareForHandoff() {
50996
+ const params2 = this.reuseParams;
50997
+ if (!this._recycleMode || !this.supportsRecycling() || !params2 || !this._browserContextId || this._closedStatus !== "open" || this._recyclingPrevented)
50998
+ return;
50999
+ if (this._clientCertificatesProxy || this.tracing.harRecorders.size || params2.recordVideo)
51000
+ return;
51001
+ const pending = this._pendingRecycle;
51002
+ this._pendingRecycle = void 0;
51003
+ if (!await (pending ?? this._recycle(nullProgress)))
51004
+ return;
51005
+ const rest = { ...params2 };
51006
+ delete rest.storageState;
51007
+ if (!isPlainJSON(rest))
51008
+ return;
51009
+ return { id: this._browserContextId, params: rest, origins: [...this._origins] };
51010
+ }
50759
51011
  static reusableContextHash(params2) {
50760
51012
  const paramsCopy = { ...params2 };
50761
51013
  if (paramsCopy.selectorEngines?.length === 0)
@@ -51910,6 +52162,13 @@ var init_browser = __esm({
51910
52162
  Disconnected: "disconnected"
51911
52163
  };
51912
52164
  }
52165
+ // Flashwright: see handoffForSession and adoptHandedOverContext.
52166
+ doAdoptContext(contextId4, options) {
52167
+ throw new Error("Not supported");
52168
+ }
52169
+ async doDetachForHandoff() {
52170
+ return void 0;
52171
+ }
51913
52172
  sdkLanguage() {
51914
52173
  return this.options.sdkLanguage || this.attribution.playwright.options.sdkLanguage;
51915
52174
  }
@@ -51964,6 +52223,7 @@ var init_browser = __esm({
51964
52223
  await this._contextForReuse.context.close(progress2, { reason: "Context reused" });
51965
52224
  }
51966
52225
  const context = await this.newContext(progress2, params2, recycle);
52226
+ context.reuseParams = params2;
51967
52227
  this._contextForReuse = { context, hash };
51968
52228
  return context;
51969
52229
  }
@@ -51976,8 +52236,12 @@ var init_browser = __esm({
51976
52236
  // (newContextForReuse), which also applies the test's storage state; a test asking for a different
51977
52237
  // context gets a new one and this one closes.
51978
52238
  async prepareRecycledContext(params2, options = {}) {
51979
- if (this._contextForReuse)
52239
+ const reuse = this._contextForReuse;
52240
+ if (reuse) {
52241
+ if (reuse.prepared && options.storageState && reuse.hash === BrowserContext.reusableContextHash(params2) + ":recycle")
52242
+ reuse.context.prepareStorageStatePage();
51980
52243
  return;
52244
+ }
51981
52245
  const context = await this.newContext(
51982
52246
  nullProgress,
51983
52247
  params2,
@@ -51985,11 +52249,61 @@ var init_browser = __esm({
51985
52249
  false
51986
52250
  /* announce */
51987
52251
  );
52252
+ context.reuseParams = params2;
51988
52253
  this._contextForReuse = { context, hash: BrowserContext.reusableContextHash(params2) + ":recycle", prepared: true };
51989
52254
  context.startRecycle();
51990
52255
  if (options.storageState)
51991
52256
  context.prepareStorageStatePage();
51992
52257
  }
52258
+ // Flashwright: a browser handed over by another process brings the context its tests used, with
52259
+ // the tab that keeps the renderer warm. It becomes the prepared recycled context, like
52260
+ // prepareRecycledContext's, and is reset once more before a test takes it.
52261
+ async adoptHandedOverContext(handedOver) {
52262
+ const params2 = { ...handedOver.params };
52263
+ const options = { ...params2 };
52264
+ validateBrowserContextOptions(options, this.options);
52265
+ const context = await this.doAdoptContext(handedOver.id, options);
52266
+ context.reuseParams = params2;
52267
+ context.setRecycleMode(true);
52268
+ context.addVisitedOrigins(handedOver.origins);
52269
+ this._contextForReuse = { context, hash: BrowserContext.reusableContextHash(params2) + ":recycle", prepared: true };
52270
+ }
52271
+ // Called once the handed-over context's existing tab is attached.
52272
+ startAdoptedContextRecycle() {
52273
+ if (this._contextForReuse?.prepared)
52274
+ this._contextForReuse.context.startRecycle();
52275
+ }
52276
+ // Flashwright: in a warm test session, the worker that launched this browser hands it to the
52277
+ // next run instead of closing it (see browserHandoff.ts). Only a browser whose one open context
52278
+ // is the recycled one, reset and healthy, is handed over; anything else returns undefined and
52279
+ // the caller closes the browser as usual. Afterwards this object is disconnected from it.
52280
+ // A headed browser is not handed over: its window would stay open between runs.
52281
+ async handoffForSession() {
52282
+ const reuse = this._contextForReuse;
52283
+ const release = this.options.browserProcess.release;
52284
+ if (!release || !this.options.launchKey || this.options.persistent || this.options.headful || this._startedClosing || !this.isConnected())
52285
+ return;
52286
+ if (!reuse || !reuse.hash.endsWith(":recycle") || this.contexts().some((context) => context !== reuse.context))
52287
+ return;
52288
+ const handedOverContext = await reuse.context.prepareForHandoff();
52289
+ if (!handedOverContext)
52290
+ return;
52291
+ const pipes = await this.doDetachForHandoff().catch(() => void 0);
52292
+ if (!pipes)
52293
+ return;
52294
+ const { pid, tempDirectories } = release();
52295
+ this._startedClosing = true;
52296
+ return {
52297
+ ...pipes,
52298
+ metadata: {
52299
+ browserName: this.options.name,
52300
+ launchKey: this.options.launchKey,
52301
+ pid,
52302
+ tempDirectories,
52303
+ context: handedOverContext
52304
+ }
52305
+ };
52306
+ }
51993
52307
  // Flashwright: see BrowserContext.outputDirsChanged.
51994
52308
  async setOutputDirs(artifactsDir, tracesDir) {
51995
52309
  this.options.artifactsDir = artifactsDir;
@@ -74532,6 +74846,7 @@ __export(utils_exports, {
74532
74846
  ZipFile: () => ZipFile,
74533
74847
  Zone: () => Zone,
74534
74848
  addSuffixToFilePath: () => addSuffixToFilePath,
74849
+ adoptProcess: () => adoptProcess,
74535
74850
  blendWithWhite: () => blendWithWhite,
74536
74851
  calculateSha1: () => calculateSha1,
74537
74852
  canAccessFile: () => canAccessFile,
@@ -79231,7 +79546,7 @@ var utilsFacade = [
79231
79546
  [["hostPlatform", "isOfficiallySupportedPlatform", "shortPlatform"], () => (init_hostPlatform(), __toCommonJS(hostPlatform_exports))],
79232
79547
  [["HttpServer", "computeAllowedHosts", "isAllowedHost", "serveFolder", "urlHostFromAddress"], () => (init_httpServer(), __toCommonJS(httpServer_exports))],
79233
79548
  [["NET_DEFAULT_TIMEOUT", "createHttp2Server", "createHttpServer", "createHttpsServer", "createProxyAgent", "createSocket", "decorateServer", "flattenAggregateError", "happyEyeballsOptions", "httpRequest", "isURLAvailable", "proxySettingsFromString", "startHttpServer"], () => (init_network(), __toCommonJS(network_exports))],
79234
- [["envArrayToObject", "gracefullyCloseAll", "gracefullyCloseSet", "gracefullyProcessExitDoNotHang", "killProcessTree", "launchProcess", "spawnAsync"], () => (init_processLauncher(), __toCommonJS(processLauncher_exports))],
79549
+ [["adoptProcess", "envArrayToObject", "gracefullyCloseAll", "gracefullyCloseSet", "gracefullyProcessExitDoNotHang", "killProcessTree", "launchProcess", "spawnAsync"], () => (init_processLauncher(), __toCommonJS(processLauncher_exports))],
79235
79550
  [["SerializedFS"], () => (init_serializedFS(), __toCommonJS(serializedFS_exports))],
79236
79551
  [["SocksProxy", "SocksProxyHandler", "parsePattern"], () => (init_socksProxy(), __toCommonJS(socksProxy_exports))],
79237
79552
  [["captureRawStack", "coreDir", "filterStackFile", "filteredStackTrace", "parseErrorStack", "parseStackFrame", "rewriteErrorMessage", "setBoxedStackPrefixes", "setCoreDir", "setRawStackFormatter", "showInternalStackFrames", "splitErrorMessage", "stringifyStackFrames", "withShallowStack"], () => (init_stackTrace(), __toCommonJS(stackTrace_exports))],
@@ -1 +1 @@
1
- import{D as e,_ as t,b as n,g as r,i,k as a,n as o,o as s,r as c,s as l,t as u,v as d}from"./assets/defaultSettingsView-DoonTZ3z.js";import"./assets/urlMatch-L3liM589.js";var f=a(e(),1),p=a(d(),1),m=r(),h=()=>{let[e,n]=f.useState(!1),[r,a]=f.useState(),[d,p]=f.useState(),[h,_]=f.useState(g),[v,y]=f.useState({done:0,total:0}),[b,x]=f.useState(!1),[S,C]=f.useState(null),[w,T]=f.useState(null),[E,D]=f.useState(!1),O=f.useCallback(e=>{let t=new URL(window.location.href);if(!e.length)return;let n=e.item(0),r=URL.createObjectURL(n);t.searchParams.append(`trace`,r);let i=t.toString();window.history.pushState({},``,i),a(r),p(n.name),x(!1),C(null)},[]);f.useEffect(()=>{let e=async e=>{if(!e.clipboardData?.files.length)return;let t=[`application/zip`,`application/x-zip-compressed`];for(let n of e.clipboardData.files)if(!t.includes(n.type))return;e.preventDefault(),O(e.clipboardData.files)};return document.addEventListener(`paste`,e),()=>document.removeEventListener(`paste`,e)}),f.useEffect(()=>{let e=e=>{let{method:t,params:n}=e.data;if(t!==`load`||!(n?.trace instanceof Blob))return;let r=new File([n.trace],`trace.zip`,{type:`application/zip`}),i=new DataTransfer;i.items.add(r),O(i.files)};return window.addEventListener(`message`,e),window.opener?.postMessage({method:`ready`},`*`),window.parent!==window&&window.parent.postMessage({method:`ready`},`*`),()=>window.removeEventListener(`message`,e)},[O]);let k=f.useCallback(e=>{e.preventDefault(),O(e.dataTransfer.files)},[O]),A=f.useCallback(e=>{e.preventDefault(),e.target.files&&O(e.target.files)},[O]);f.useEffect(()=>{let e=new URL(window.location.href).searchParams,t=e.get(`trace`);if(n(e.has(`isServer`)),t?.startsWith(`file:`)){T(t||null);return}if(e.has(`isServer`)){let e=new URLSearchParams(window.location.search).get(`ws`),t=new URL(`../${e}`,window.location.toString());t.protocol=window.location.protocol===`https:`?`wss:`:`ws:`;let n=new o(new c(t));n.onLoadTraceRequested(async e=>{a(e.traceUrl),x(!1),C(null)}),n.initialize({}).catch(()=>{})}else t&&!t.startsWith(`blob:`)&&a(t)},[]);let j=f.useCallback(async e=>{let n=new URLSearchParams;n.set(`trace`,e);let r=await fetch(`contexts?${n.toString()}`);if(!r.ok){let{error:e}=await r.json();return C(e),e}let i=new t(e,await r.json());y({done:0,total:0}),C(null),_(i)},[]);f.useEffect(()=>{(async()=>{if(!r){_(g);return}let t=e=>{e.data.method===`progress`&&y(e.data.params)};try{navigator.serviceWorker.addEventListener(`message`,t),y({done:0,total:1});let n=await j(r);n?.includes(`please grant permission for Local Network Access`)&&(await fetch(r,{method:`HEAD`,headers:{"x-pw-serviceworker":`skip`}}),n=await j(r)),n&&(e||a(void 0))}finally{navigator.serviceWorker.removeEventListener(`message`,t)}})()},[e,r,d,j]);let M=v.done!==v.total&&v.total!==0&&!S;f.useEffect(()=>{if(M){let e=setTimeout(()=>{D(!0)},200);return()=>clearTimeout(e)}else D(!1)},[M]);let N=!!(!e&&!b&&!w&&(!r||S));return(0,m.jsxs)(`div`,{className:`vbox workbench-loader`,onDragOver:e=>{e.preventDefault(),e.dataTransfer.types.includes(`Files`)&&x(!0)},children:[(0,m.jsxs)(`div`,{className:`hbox workbench-loader-header`,...N?{inert:!0}:{},children:[(0,m.jsx)(`div`,{className:`logo`,children:(0,m.jsx)(`img`,{src:`playwright-logo.svg`,alt:`Playwright logo`})}),(0,m.jsx)(`div`,{className:`product`,children:`Playwright`}),h.title&&(0,m.jsx)(`div`,{className:`title`,children:h.title}),(0,m.jsx)(`div`,{className:`spacer`}),(0,m.jsx)(s,{icon:`settings-gear`,title:`Settings`,dialogDataTestId:`settings-toolbar-dialog`,children:(0,m.jsx)(u,{location:`trace-viewer`,model:h})})]}),(0,m.jsx)(i,{model:h,inert:N}),w&&(0,m.jsxs)(`div`,{className:`drop-target`,children:[(0,m.jsx)(`div`,{children:`Trace Viewer uses Service Workers to show traces. To view trace:`}),(0,m.jsxs)(`div`,{style:{paddingTop:20},children:[(0,m.jsxs)(`div`,{children:[`1. Click `,(0,m.jsx)(`a`,{href:w,children:`here`}),` to put your trace into the download shelf`]}),(0,m.jsxs)(`div`,{children:[`2. Go to `,(0,m.jsx)(`a`,{href:`https://trace.playwright.dev`,children:`trace.playwright.dev`})]}),(0,m.jsx)(`div`,{children:`3. Drop the trace from the download shelf into the page`})]})]}),(0,m.jsx)(l,{open:E,isModal:!0,className:`progress-dialog`,children:(0,m.jsxs)(`div`,{className:`progress-content`,children:[(0,m.jsx)(`div`,{className:`title`,role:`heading`,"aria-level":1,children:`Loading Playwright Trace...`}),(0,m.jsx)(`div`,{className:`progress-wrapper`,children:(0,m.jsx)(`div`,{className:`inner-progress`,style:{width:v.total?100*v.done/v.total+`%`:0}})})]})}),N&&(0,m.jsxs)(`div`,{className:`drop-target`,children:[(0,m.jsx)(`div`,{className:`processing-error`,role:`alert`,children:S}),(0,m.jsx)(`div`,{className:`title`,role:`heading`,"aria-level":1,children:`Drop Playwright Trace to load`}),(0,m.jsx)(`div`,{children:`or`}),(0,m.jsx)(`button`,{onClick:()=>{let e=document.createElement(`input`);e.type=`file`,e.click(),e.addEventListener(`change`,e=>A(e))},type:`button`,children:`Select file`}),(0,m.jsx)(`div`,{className:`info`,children:`Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere, it opens it locally.`}),(0,m.jsxs)(`div`,{className:`version`,children:[`Playwright v`,void 0]})]}),e&&!r&&(0,m.jsx)(`div`,{className:`drop-target`,children:(0,m.jsx)(`div`,{className:`title`,children:`Select test to see the trace`})}),b&&(0,m.jsx)(`div`,{className:`drop-target`,onDragLeave:()=>{x(!1)},onDrop:e=>k(e),children:(0,m.jsx)(`div`,{className:`title`,children:`Release to analyse the Playwright Trace`})})]})},g=new t(``,[]),_=({traceJson:e})=>{let[n,r]=f.useState(void 0),[a,o]=f.useState(0),s=f.useRef(null);return f.useEffect(()=>(s.current&&clearTimeout(s.current),s.current=setTimeout(async()=>{try{r(await v(e))}catch{r(new t(``,[]))}finally{o(a+1)}},500),()=>{s.current&&clearTimeout(s.current)}),[e,a]),(0,m.jsx)(i,{isLive:!0,model:n})};async function v(e){let n=new URLSearchParams;return n.set(`trace`,e),new t(e,await(await fetch(`contexts?${n.toString()}`)).json())}(async()=>{let e=new URLSearchParams(window.location.search);if(n(),window.location.protocol!==`file:`){if(!navigator.serviceWorker)throw Error(`Service workers are not supported.\nMake sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`);navigator.serviceWorker.register(`sw.bundle.js`),navigator.serviceWorker.controller||await new Promise(e=>{navigator.serviceWorker.oncontrollerchange=()=>e()}),setInterval(function(){fetch(`ping`)},1e4)}let t=e.get(`trace`),r=t?.endsWith(`.json`)?(0,m.jsx)(_,{traceJson:t}):(0,m.jsx)(h,{});p.createRoot(document.querySelector(`#root`)).render(r)})();
1
+ import{D as e,_ as t,b as n,g as r,i,k as a,n as o,o as s,r as c,s as l,t as u,v as d}from"./assets/defaultSettingsView-DoonTZ3z.js";import"./assets/urlMatch-L3liM589.js";var f=a(e(),1),p=a(d(),1),m=r(),h=()=>{let[e,n]=f.useState(!1),[r,a]=f.useState(),[d,p]=f.useState(),[h,_]=f.useState(g),[v,y]=f.useState({done:0,total:0}),[b,x]=f.useState(!1),[S,C]=f.useState(null),[w,T]=f.useState(null),[E,D]=f.useState(!1),O=f.useCallback(e=>{let t=new URL(window.location.href);if(!e.length)return;let n=e.item(0),r=URL.createObjectURL(n);t.searchParams.append(`trace`,r);let i=t.toString();window.history.pushState({},``,i),a(r),p(n.name),x(!1),C(null)},[]);f.useEffect(()=>{let e=async e=>{if(!e.clipboardData?.files.length)return;let t=[`application/zip`,`application/x-zip-compressed`];for(let n of e.clipboardData.files)if(!t.includes(n.type))return;e.preventDefault(),O(e.clipboardData.files)};return document.addEventListener(`paste`,e),()=>document.removeEventListener(`paste`,e)}),f.useEffect(()=>{let e=e=>{let{method:t,params:n}=e.data;if(t!==`load`||!(n?.trace instanceof Blob))return;let r=new File([n.trace],`trace.zip`,{type:`application/zip`}),i=new DataTransfer;i.items.add(r),O(i.files)};return window.addEventListener(`message`,e),window.opener?.postMessage({method:`ready`},`*`),window.parent!==window&&window.parent.postMessage({method:`ready`},`*`),()=>window.removeEventListener(`message`,e)},[O]);let k=f.useCallback(e=>{e.preventDefault(),O(e.dataTransfer.files)},[O]),A=f.useCallback(e=>{e.preventDefault(),e.target.files&&O(e.target.files)},[O]);f.useEffect(()=>{let e=new URL(window.location.href).searchParams,t=e.get(`trace`);if(n(e.has(`isServer`)),t?.startsWith(`file:`)){T(t||null);return}if(e.has(`isServer`)){let e=new URLSearchParams(window.location.search).get(`ws`),t=new URL(`../${e}`,window.location.toString());t.protocol=window.location.protocol===`https:`?`wss:`:`ws:`;let n=new o(new c(t));n.onLoadTraceRequested(async e=>{a(e.traceUrl),x(!1),C(null)}),n.initialize({}).catch(()=>{})}else t&&!t.startsWith(`blob:`)&&a(t)},[]);let j=f.useCallback(async e=>{let n=new URLSearchParams;n.set(`trace`,e);let r=await fetch(`contexts?${n.toString()}`);if(!r.ok){let{error:e}=await r.json();return C(e),e}let i=new t(e,await r.json());y({done:0,total:0}),C(null),_(i)},[]);f.useEffect(()=>{(async()=>{if(!r){_(g);return}let t=e=>{e.data.method===`progress`&&y(e.data.params)};try{navigator.serviceWorker.addEventListener(`message`,t),y({done:0,total:1});let n=await j(r);n?.includes(`please grant permission for Local Network Access`)&&(await fetch(r,{method:`HEAD`,headers:{"x-pw-serviceworker":`skip`}}),n=await j(r)),n&&(e||a(void 0))}finally{navigator.serviceWorker.removeEventListener(`message`,t)}})()},[e,r,d,j]);let M=v.done!==v.total&&v.total!==0&&!S;f.useEffect(()=>{if(M){let e=setTimeout(()=>{D(!0)},200);return()=>clearTimeout(e)}else D(!1)},[M]);let N=!!(!e&&!b&&!w&&(!r||S));return(0,m.jsxs)(`div`,{className:`vbox workbench-loader`,onDragOver:e=>{e.preventDefault(),e.dataTransfer.types.includes(`Files`)&&x(!0)},children:[(0,m.jsxs)(`div`,{className:`hbox workbench-loader-header`,...N?{inert:!0}:{},children:[(0,m.jsx)(`div`,{className:`logo`,children:(0,m.jsx)(`img`,{src:`playwright-logo.svg`,alt:`Playwright logo`})}),(0,m.jsx)(`div`,{className:`product`,children:`Playwright`}),h.title&&(0,m.jsx)(`div`,{className:`title`,children:h.title}),(0,m.jsx)(`div`,{className:`spacer`}),(0,m.jsx)(s,{icon:`settings-gear`,title:`Settings`,dialogDataTestId:`settings-toolbar-dialog`,children:(0,m.jsx)(u,{location:`trace-viewer`,model:h})})]}),(0,m.jsx)(i,{model:h,inert:N}),w&&(0,m.jsxs)(`div`,{className:`drop-target`,children:[(0,m.jsx)(`div`,{children:`Trace Viewer uses Service Workers to show traces. To view trace:`}),(0,m.jsxs)(`div`,{style:{paddingTop:20},children:[(0,m.jsxs)(`div`,{children:[`1. Click `,(0,m.jsx)(`a`,{href:w,children:`here`}),` to put your trace into the download shelf`]}),(0,m.jsxs)(`div`,{children:[`2. Go to `,(0,m.jsx)(`a`,{href:`https://trace.playwright.dev`,children:`trace.playwright.dev`})]}),(0,m.jsx)(`div`,{children:`3. Drop the trace from the download shelf into the page`})]})]}),(0,m.jsx)(l,{open:E,isModal:!0,className:`progress-dialog`,children:(0,m.jsxs)(`div`,{className:`progress-content`,children:[(0,m.jsx)(`div`,{className:`title`,role:`heading`,"aria-level":1,children:`Loading Playwright Trace...`}),(0,m.jsx)(`div`,{className:`progress-wrapper`,children:(0,m.jsx)(`div`,{className:`inner-progress`,style:{width:v.total?100*v.done/v.total+`%`:0}})})]})}),N&&(0,m.jsxs)(`div`,{className:`drop-target`,children:[(0,m.jsx)(`div`,{className:`processing-error`,role:`alert`,children:S}),(0,m.jsx)(`div`,{className:`title`,role:`heading`,"aria-level":1,children:`Drop Playwright Trace to load`}),(0,m.jsx)(`div`,{children:`or`}),(0,m.jsx)(`button`,{onClick:()=>{let e=document.createElement(`input`);e.type=`file`,e.click(),e.addEventListener(`change`,e=>A(e))},type:`button`,children:`Select file`}),(0,m.jsx)(`div`,{className:`info`,children:`Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere, it opens it locally.`}),(0,m.jsxs)(`div`,{className:`version`,children:[`Playwright v`,`1.64.1`]})]}),e&&!r&&(0,m.jsx)(`div`,{className:`drop-target`,children:(0,m.jsx)(`div`,{className:`title`,children:`Select test to see the trace`})}),b&&(0,m.jsx)(`div`,{className:`drop-target`,onDragLeave:()=>{x(!1)},onDrop:e=>k(e),children:(0,m.jsx)(`div`,{className:`title`,children:`Release to analyse the Playwright Trace`})})]})},g=new t(``,[]),_=({traceJson:e})=>{let[n,r]=f.useState(void 0),[a,o]=f.useState(0),s=f.useRef(null);return f.useEffect(()=>(s.current&&clearTimeout(s.current),s.current=setTimeout(async()=>{try{r(await v(e))}catch{r(new t(``,[]))}finally{o(a+1)}},500),()=>{s.current&&clearTimeout(s.current)}),[e,a]),(0,m.jsx)(i,{isLive:!0,model:n})};async function v(e){let n=new URLSearchParams;return n.set(`trace`,e),new t(e,await(await fetch(`contexts?${n.toString()}`)).json())}(async()=>{let e=new URLSearchParams(window.location.search);if(n(),window.location.protocol!==`file:`){if(!navigator.serviceWorker)throw Error(`Service workers are not supported.\nMake sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`);navigator.serviceWorker.register(`sw.bundle.js`),navigator.serviceWorker.controller||await new Promise(e=>{navigator.serviceWorker.oncontrollerchange=()=>e()}),setInterval(function(){fetch(`ping`)},1e4)}let t=e.get(`trace`),r=t?.endsWith(`.json`)?(0,m.jsx)(_,{traceJson:t}):(0,m.jsx)(h,{});p.createRoot(document.querySelector(`#root`)).render(r)})();
@@ -7,7 +7,7 @@
7
7
  <link rel="icon" href="./playwright-logo.svg" type="image/svg+xml">
8
8
  <link rel="manifest" href="./manifest.webmanifest">
9
9
  <title>Playwright Trace Viewer</title>
10
- <script type="module" crossorigin src="./index.Bdvc0_Yv.js"></script>
10
+ <script type="module" crossorigin src="./index.B5jyRlOV.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="./assets/urlMatch-L3liM589.js">
12
12
  <link rel="modulepreload" crossorigin href="./assets/defaultSettingsView-DoonTZ3z.js">
13
13
  <link rel="stylesheet" crossorigin href="./defaultSettingsView.C8BeHuxd.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flashwright-core",
3
- "version": "1.64.1-flashwright.1",
3
+ "version": "1.64.1-flashwright.2",
4
4
  "description": "Flashwright: a faster drop-in replacement for Playwright (fork of Playwright 1.64.1)",
5
5
  "repository": {
6
6
  "type": "git",