playwright-core 1.58.0-alpha-2025-12-09 → 1.58.0-alpha-2025-12-11

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.
Files changed (37) hide show
  1. package/ThirdPartyNotices.txt +3 -3
  2. package/browsers.json +6 -6
  3. package/lib/client/browser.js +3 -5
  4. package/lib/client/browserType.js +2 -2
  5. package/lib/client/fetch.js +2 -4
  6. package/lib/mcpBundleImpl/index.js +29 -29
  7. package/lib/protocol/serializers.js +5 -0
  8. package/lib/server/agent/actionRunner.js +33 -2
  9. package/lib/server/agent/agent.js +18 -12
  10. package/lib/server/agent/context.js +26 -11
  11. package/lib/server/agent/tools.js +67 -5
  12. package/lib/server/artifact.js +1 -1
  13. package/lib/server/bidi/bidiBrowser.js +13 -4
  14. package/lib/server/bidi/bidiPage.js +9 -1
  15. package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
  16. package/lib/server/chromium/crPage.js +5 -5
  17. package/lib/server/dispatchers/dispatcher.js +5 -8
  18. package/lib/server/firefox/ffBrowser.js +1 -1
  19. package/lib/server/firefox/ffPage.js +1 -1
  20. package/lib/server/instrumentation.js +3 -0
  21. package/lib/server/page.js +2 -2
  22. package/lib/server/progress.js +2 -0
  23. package/lib/server/screencast.js +24 -25
  24. package/lib/server/utils/network.js +18 -26
  25. package/lib/server/videoRecorder.js +20 -11
  26. package/lib/server/webkit/wkBrowser.js +1 -1
  27. package/lib/server/webkit/wkPage.js +7 -7
  28. package/lib/vite/traceViewer/index.BVu7tZDe.css +1 -0
  29. package/lib/vite/traceViewer/index.html +2 -2
  30. package/lib/vite/traceViewer/index.zFV_GQE-.js +2 -0
  31. package/lib/vite/traceViewer/sw.bundle.js +3 -1
  32. package/lib/vite/traceViewer/uiMode.DiEbKRwa.js +5 -0
  33. package/lib/vite/traceViewer/uiMode.html +1 -1
  34. package/package.json +1 -1
  35. package/lib/vite/traceViewer/index.C4Y3Aw8n.css +0 -1
  36. package/lib/vite/traceViewer/index.YskCIlQ-.js +0 -2
  37. package/lib/vite/traceViewer/uiMode.CmFFBCQb.js +0 -5
@@ -41,16 +41,13 @@ module.exports = __toCommonJS(network_exports);
41
41
  var import_http = __toESM(require("http"));
42
42
  var import_http2 = __toESM(require("http2"));
43
43
  var import_https = __toESM(require("https"));
44
- var import_url = __toESM(require("url"));
45
44
  var import_utilsBundle = require("../../utilsBundle");
46
45
  var import_happyEyeballs = require("./happyEyeballs");
47
46
  var import_manualPromise = require("../../utils/isomorphic/manualPromise");
48
47
  const NET_DEFAULT_TIMEOUT = 3e4;
49
48
  function httpRequest(params, onResponse, onError) {
50
- const parsedUrl = import_url.default.parse(params.url);
51
- let options = {
52
- ...parsedUrl,
53
- agent: parsedUrl.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent,
49
+ let url = new URL(params.url);
50
+ const options = {
54
51
  method: params.method || "GET",
55
52
  headers: params.headers
56
53
  };
@@ -58,21 +55,16 @@ function httpRequest(params, onResponse, onError) {
58
55
  options.rejectUnauthorized = params.rejectUnauthorized;
59
56
  const proxyURL = (0, import_utilsBundle.getProxyForUrl)(params.url);
60
57
  if (proxyURL) {
58
+ const parsedProxyURL = normalizeProxyURL(proxyURL);
61
59
  if (params.url.startsWith("http:")) {
62
- const parsedProxyURL = import_url.default.parse(proxyURL);
63
- options = {
64
- path: parsedUrl.href,
65
- host: parsedProxyURL.hostname,
66
- port: parsedProxyURL.port,
67
- protocol: parsedProxyURL.protocol || "http:",
68
- headers: options.headers,
69
- method: options.method
70
- };
60
+ parsedProxyURL.pathname = url.toString();
61
+ url = parsedProxyURL;
71
62
  } else {
72
- options.agent = new import_utilsBundle.HttpsProxyAgent(normalizeProxyURL(proxyURL));
63
+ options.agent = new import_utilsBundle.HttpsProxyAgent(parsedProxyURL);
73
64
  options.rejectUnauthorized = false;
74
65
  }
75
66
  }
67
+ options.agent ??= url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent;
76
68
  let cancelRequest;
77
69
  const requestCallback = (res) => {
78
70
  const statusCode = res.statusCode || 0;
@@ -83,7 +75,7 @@ function httpRequest(params, onResponse, onError) {
83
75
  onResponse(res);
84
76
  }
85
77
  };
86
- const request = options.protocol === "https:" ? import_https.default.request(options, requestCallback) : import_http.default.request(options, requestCallback);
78
+ const request = url.protocol === "https:" ? import_https.default.request(url, options, requestCallback) : import_http.default.request(url, options, requestCallback);
87
79
  request.on("error", onError);
88
80
  if (params.socketTimeout !== void 0) {
89
81
  request.setTimeout(params.socketTimeout, () => {
@@ -122,7 +114,7 @@ async function fetchData(progress, params, onError) {
122
114
  throw error;
123
115
  }
124
116
  }
125
- function shouldBypassProxy(url2, bypass) {
117
+ function shouldBypassProxy(url, bypass) {
126
118
  if (!bypass)
127
119
  return false;
128
120
  const domains = bypass.split(",").map((s) => {
@@ -131,7 +123,7 @@ function shouldBypassProxy(url2, bypass) {
131
123
  s = "." + s;
132
124
  return s;
133
125
  });
134
- const domain = "." + url2.hostname;
126
+ const domain = "." + url.hostname;
135
127
  return domains.some((d) => domain.endsWith(d));
136
128
  }
137
129
  function normalizeProxyURL(proxy) {
@@ -177,20 +169,20 @@ function createHttp2Server(...args) {
177
169
  decorateServer(server);
178
170
  return server;
179
171
  }
180
- async function isURLAvailable(url2, ignoreHTTPSErrors, onLog, onStdErr) {
181
- let statusCode = await httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr);
182
- if (statusCode === 404 && url2.pathname === "/") {
183
- const indexUrl = new URL(url2);
172
+ async function isURLAvailable(url, ignoreHTTPSErrors, onLog, onStdErr) {
173
+ let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
174
+ if (statusCode === 404 && url.pathname === "/") {
175
+ const indexUrl = new URL(url);
184
176
  indexUrl.pathname = "/index.html";
185
177
  statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr);
186
178
  }
187
179
  return statusCode >= 200 && statusCode < 404;
188
180
  }
189
- async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
181
+ async function httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr) {
190
182
  return new Promise((resolve) => {
191
- onLog?.(`HTTP GET: ${url2}`);
183
+ onLog?.(`HTTP GET: ${url}`);
192
184
  httpRequest({
193
- url: url2.toString(),
185
+ url: url.toString(),
194
186
  headers: { Accept: "*/*" },
195
187
  rejectUnauthorized: !ignoreHTTPSErrors
196
188
  }, (res) => {
@@ -201,7 +193,7 @@ async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
201
193
  }, (error) => {
202
194
  if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT")
203
195
  onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`);
204
- onLog?.(`Error while checking if ${url2} is available: ${error.message}`);
196
+ onLog?.(`Error while checking if ${url} is available: ${error.message}`);
205
197
  resolve(0);
206
198
  });
207
199
  });
@@ -22,11 +22,10 @@ __export(videoRecorder_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(videoRecorder_exports);
24
24
  var import_utils = require("../utils");
25
- var import_page = require("./page");
26
25
  var import_processLauncher = require("./utils/processLauncher");
27
26
  const fps = 25;
28
27
  class VideoRecorder {
29
- constructor(page, ffmpegPath) {
28
+ constructor(ffmpegPath, options) {
30
29
  this._process = null;
31
30
  this._gracefullyClose = null;
32
31
  this._lastWritePromise = Promise.resolve();
@@ -36,16 +35,12 @@ class VideoRecorder {
36
35
  this._frameQueue = [];
37
36
  this._isStopped = false;
38
37
  this._ffmpegPath = ffmpegPath;
39
- page.on(import_page.Page.Events.ScreencastFrame, (frame) => this.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3));
40
- }
41
- static async launch(page, ffmpegPath, options) {
42
38
  if (!options.outputFile.endsWith(".webm"))
43
39
  throw new Error("File must have .webm extension");
44
- const recorder = new VideoRecorder(page, ffmpegPath);
45
- await recorder._launch(options);
46
- return recorder;
40
+ this._launchPromise = this._launch(options).catch((e) => e);
47
41
  }
48
42
  async _launch(options) {
43
+ await (0, import_utils.mkdirIfNeeded)(options.outputFile);
49
44
  const w = options.width;
50
45
  const h = options.height;
51
46
  const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" ");
@@ -74,6 +69,13 @@ class VideoRecorder {
74
69
  this._gracefullyClose = gracefullyClose;
75
70
  }
76
71
  writeFrame(frame, timestamp) {
72
+ this._launchPromise.then((error) => {
73
+ if (error)
74
+ return;
75
+ this._writeFrame(frame, timestamp);
76
+ });
77
+ }
78
+ _writeFrame(frame, timestamp) {
77
79
  (0, import_utils.assert)(this._process);
78
80
  if (this._isStopped)
79
81
  return;
@@ -100,13 +102,20 @@ class VideoRecorder {
100
102
  });
101
103
  }
102
104
  async stop() {
105
+ const error = await this._launchPromise;
106
+ if (error)
107
+ throw error;
103
108
  if (this._isStopped || !this._lastFrame)
104
109
  return;
105
110
  const addTime = Math.max(((0, import_utils.monotonicTime)() - this._lastWriteNodeTime) / 1e3, 1);
106
- this.writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
111
+ this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
107
112
  this._isStopped = true;
108
- await this._lastWritePromise;
109
- await this._gracefullyClose();
113
+ try {
114
+ await this._lastWritePromise;
115
+ await this._gracefullyClose();
116
+ } catch (e) {
117
+ import_utils.debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
118
+ }
110
119
  }
111
120
  }
112
121
  // Annotate the CommonJS export names for ESM import in node:
@@ -79,7 +79,7 @@ class WKBrowser extends import_browser.Browser {
79
79
  wkPage.didClose();
80
80
  this._wkPages.clear();
81
81
  for (const video of this._idToVideo.values())
82
- video.artifact.reportFinished(new import_errors.TargetClosedError());
82
+ video.artifact.reportFinished(new import_errors.TargetClosedError(this.closeReason()));
83
83
  this._idToVideo.clear();
84
84
  this._didClose();
85
85
  }
@@ -245,7 +245,7 @@ class WKPage {
245
245
  this._provisionalPage.dispose();
246
246
  this._provisionalPage = null;
247
247
  }
248
- this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError());
248
+ this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError(this._page.closeReason()));
249
249
  this._page._didClose();
250
250
  }
251
251
  dispatchMessageToSession(message) {
@@ -441,7 +441,7 @@ class WKPage {
441
441
  }
442
442
  async navigateFrame(frame, url, referrer) {
443
443
  if (this._pageProxySession.isDisposed())
444
- throw new import_errors.TargetClosedError();
444
+ throw new import_errors.TargetClosedError(this._page.closeReason());
445
445
  const pageProxyId = this._pageProxySession.sessionId;
446
446
  const result = await this._pageProxySession.connection.browserSession.send("Playwright.navigate", { url, pageProxyId, frameId: frame._id, referrer });
447
447
  return { newDocumentId: result.loaderId };
@@ -750,9 +750,10 @@ class WKPage {
750
750
  return 0;
751
751
  }
752
752
  async _initializeVideoRecording() {
753
- const screencastOptions = await this._page.screencast.initializeVideoRecorder();
754
- if (screencastOptions)
755
- await this._page.screencast.startVideoRecording(screencastOptions);
753
+ const screencast = this._page.screencast;
754
+ const videoOptions = screencast.launchVideoRecorder();
755
+ if (videoOptions)
756
+ await screencast.startVideoRecording(videoOptions);
756
757
  }
757
758
  validateScreenshotDimension(side, omitDeviceScaleFactor) {
758
759
  if (process.platform === "darwin")
@@ -840,8 +841,7 @@ class WKPage {
840
841
  const buffer = Buffer.from(event.data, "base64");
841
842
  this._page.emit(import_page.Page.Events.ScreencastFrame, {
842
843
  buffer,
843
- frameSwapWallTime: event.timestamp * 1e3,
844
- // timestamp is in seconds, we need to convert to milliseconds.
844
+ frameSwapWallTime: event.timestamp ? event.timestamp * 1e3 : Date.now(),
845
845
  width: event.deviceWidth,
846
846
  height: event.deviceHeight
847
847
  });
@@ -0,0 +1 @@
1
+ .drop-target{display:flex;align-items:center;justify-content:center;flex:auto;flex-direction:column;background-color:var(--vscode-editor-background);position:absolute;top:0;right:0;bottom:0;left:0;z-index:100;line-height:24px}body .drop-target{background:#fffc}:root.dark-mode .drop-target{background:#000c}.drop-target .title{font-size:24px;font-weight:700;margin-bottom:30px}.drop-target .info{max-width:400px;text-align:center}.drop-target .processing-error{font-size:24px;color:#e74c3c;font-weight:700;text-align:center;margin:30px;white-space:pre-line}.drop-target input{margin-top:50px}.drop-target button{color:#fff;background-color:#007acc;padding:8px 12px;border:none;margin:30px 0;cursor:pointer}.drop-target .version{color:var(--vscode-disabledForeground);margin-top:8px}.progress-dialog{width:400px;top:0;right:0;bottom:0;left:0;border:none;outline:none;background-color:var(--vscode-sideBar-background)}.progress-dialog::backdrop{background-color:#0006}.progress-content{padding:16px}.progress-content .title{background-color:unset;font-size:18px;font-weight:700;padding:0}.progress-wrapper{background-color:var(--vscode-commandCenter-activeBackground);width:100%;margin-top:16px;margin-bottom:8px}.inner-progress{background-color:var(--vscode-progressBar-background);height:4px}.header{display:flex;background-color:#000;flex:none;flex-basis:48px;line-height:48px;font-size:16px;color:#ccc}.workbench-loader{contain:size}.workbench-loader .header .toolbar-button{margin:12px;padding:8px 4px}.workbench-loader .logo{margin-left:16px;display:flex;align-items:center}.workbench-loader .logo img{height:32px;width:32px;pointer-events:none;flex:none}.workbench-loader .product{font-weight:600;margin-left:16px;flex:none}.workbench-loader .header .title{margin-left:16px;overflow:hidden;text-overflow:ellipsis;text-wrap:nowrap}html,body{min-width:550px;min-height:450px;overflow:auto}
@@ -7,10 +7,10 @@
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.YskCIlQ-.js"></script>
10
+ <script type="module" crossorigin src="./index.zFV_GQE-.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="./assets/defaultSettingsView-V7hnXDpz.js">
12
12
  <link rel="stylesheet" crossorigin href="./defaultSettingsView.CpI7RarT.css">
13
- <link rel="stylesheet" crossorigin href="./index.C4Y3Aw8n.css">
13
+ <link rel="stylesheet" crossorigin href="./index.BVu7tZDe.css">
14
14
  </head>
15
15
  <body>
16
16
  <div id="root"></div>
@@ -0,0 +1,2 @@
1
+ import{T as x,r,a as F,W as M,j as e,D as I,b as z,c as P,d as A,e as O,f as V}from"./assets/defaultSettingsView-V7hnXDpz.js";const $=()=>{const[o,c]=r.useState(!1),[i,l]=r.useState(),[h,u]=r.useState(),[p,b]=r.useState(N),[f,j]=r.useState({done:0,total:0}),[k,v]=r.useState(!1),[y,m]=r.useState(null),[S,R]=r.useState(null),[U,E]=r.useState(!1),g=r.useCallback(t=>{const s=new URL(window.location.href);if(!t.length)return;const n=t.item(0),a=URL.createObjectURL(n);s.searchParams.append("trace",a);const d=s.toString();window.history.pushState({},"",d),l(a),u(n.name),v(!1),m(null)},[]);r.useEffect(()=>{const t=async s=>{var n;if((n=s.clipboardData)!=null&&n.files.length){for(const a of s.clipboardData.files)if(a.type!=="application/zip")return;s.preventDefault(),g(s.clipboardData.files)}};return document.addEventListener("paste",t),()=>document.removeEventListener("paste",t)}),r.useEffect(()=>{const t=s=>{const{method:n,params:a}=s.data;if(n!=="load"||!((a==null?void 0:a.trace)instanceof Blob))return;const d=new File([a.trace],"trace.zip",{type:"application/zip"}),w=new DataTransfer;w.items.add(d),g(w.files)};return window.addEventListener("message",t),()=>window.removeEventListener("message",t)});const W=r.useCallback(t=>{t.preventDefault(),g(t.dataTransfer.files)},[g]),C=r.useCallback(t=>{t.preventDefault(),t.target.files&&g(t.target.files)},[g]);r.useEffect(()=>{const t=new URL(window.location.href).searchParams,s=t.get("trace");if(c(t.has("isServer")),s!=null&&s.startsWith("file:")){R(s||null);return}if(t.has("isServer")){const n=new URLSearchParams(window.location.search).get("ws"),a=new URL(`../${n}`,window.location.toString());a.protocol=window.location.protocol==="https:"?"wss:":"ws:";const d=new F(new M(a));d.onLoadTraceRequested(async w=>{l(w.traceUrl),v(!1),m(null)}),d.initialize({}).catch(()=>{})}else s&&!s.startsWith("blob:")&&l(s)},[]);const T=r.useCallback(async t=>{const s=new URLSearchParams;s.set("trace",t);const n=await fetch(`contexts?${s.toString()}`);if(!n.ok){const{error:w}=await n.json();return m(w),w}const a=await n.json(),d=new x(t,a);j({done:0,total:0}),m(null),b(d)},[]);r.useEffect(()=>{(async()=>{if(!i){b(N);return}const t=s=>{s.data.method==="progress"&&j(s.data.params)};try{navigator.serviceWorker.addEventListener("message",t),j({done:0,total:1});let s=await T(i);s!=null&&s.includes("please grant permission for Local Network Access")&&(await fetch(i,{method:"HEAD",headers:{"x-pw-serviceworker":"skip"}}),s=await T(i)),s&&(o||l(void 0))}finally{navigator.serviceWorker.removeEventListener("message",t)}})()},[o,i,h,T]);const D=f.done!==f.total&&f.total!==0&&!y;r.useEffect(()=>{if(D){const t=setTimeout(()=>{E(!0)},200);return()=>clearTimeout(t)}else E(!1)},[D]);const L=!!(!o&&!k&&!S&&(!i||y));return e.jsxs("div",{className:"vbox workbench-loader",onDragOver:t=>{t.preventDefault(),t.dataTransfer.types.includes("Files")&&v(!0)},children:[e.jsxs("div",{className:"hbox header",...L?{inert:!0}:{},children:[e.jsx("div",{className:"logo",children:e.jsx("img",{src:"playwright-logo.svg",alt:"Playwright logo"})}),e.jsx("div",{className:"product",children:"Playwright"}),p.title&&e.jsx("div",{className:"title",children:p.title}),e.jsx("div",{className:"spacer"}),e.jsx(I,{icon:"settings-gear",title:"Settings",dialogDataTestId:"settings-toolbar-dialog",children:e.jsx(z,{location:"trace-viewer"})})]}),e.jsx(P,{model:p,inert:L}),S&&e.jsxs("div",{className:"drop-target",children:[e.jsx("div",{children:"Trace Viewer uses Service Workers to show traces. To view trace:"}),e.jsxs("div",{style:{paddingTop:20},children:[e.jsxs("div",{children:["1. Click ",e.jsx("a",{href:S,children:"here"})," to put your trace into the download shelf"]}),e.jsxs("div",{children:["2. Go to ",e.jsx("a",{href:"https://trace.playwright.dev",children:"trace.playwright.dev"})]}),e.jsx("div",{children:"3. Drop the trace from the download shelf into the page"})]})]}),e.jsx(A,{open:U,isModal:!0,className:"progress-dialog",children:e.jsxs("div",{className:"progress-content",children:[e.jsx("div",{className:"title",role:"heading","aria-level":1,children:"Loading Playwright Trace..."}),e.jsx("div",{className:"progress-wrapper",children:e.jsx("div",{className:"inner-progress",style:{width:f.total?100*f.done/f.total+"%":0}})})]})}),L&&e.jsxs("div",{className:"drop-target",children:[e.jsx("div",{className:"processing-error",role:"alert",children:y}),e.jsx("div",{className:"title",role:"heading","aria-level":1,children:"Drop Playwright Trace to load"}),e.jsx("div",{children:"or"}),e.jsx("button",{onClick:()=>{const t=document.createElement("input");t.type="file",t.click(),t.addEventListener("change",s=>C(s))},type:"button",children:"Select file"}),e.jsx("div",{className:"info",children:"Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere, it opens it locally."}),e.jsxs("div",{className:"version",children:["Playwright v","1.58.0-next"]})]}),o&&!i&&e.jsx("div",{className:"drop-target",children:e.jsx("div",{className:"title",children:"Select test to see the trace"})}),k&&e.jsx("div",{className:"drop-target",onDragLeave:()=>{v(!1)},onDrop:t=>W(t),children:e.jsx("div",{className:"title",children:"Release to analyse the Playwright Trace"})})]})},N=new x("",[]),q=({traceJson:o})=>{const[c,i]=r.useState(void 0),[l,h]=r.useState(0),u=r.useRef(null);return r.useEffect(()=>(u.current&&clearTimeout(u.current),u.current=setTimeout(async()=>{try{const p=await B(o);i(p)}catch{const p=new x("",[]);i(p)}finally{h(l+1)}},500),()=>{u.current&&clearTimeout(u.current)}),[o,l]),e.jsx(P,{isLive:!0,model:c})};async function B(o){const c=new URLSearchParams;c.set("trace",o);const l=await(await fetch(`contexts?${c.toString()}`)).json();return new x(o,l)}(async()=>{const o=new URLSearchParams(window.location.search);if(O(),window.location.protocol!=="file:"){if(o.get("isUnderTest")==="true"&&await new Promise(h=>setTimeout(h,1e3)),!navigator.serviceWorker)throw new Error(`Service workers are not supported.
2
+ Make sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`);navigator.serviceWorker.register("sw.bundle.js"),navigator.serviceWorker.controller||await new Promise(h=>{navigator.serviceWorker.oncontrollerchange=()=>h()}),setInterval(function(){fetch("ping")},1e4)}const c=o.get("trace"),l=(c==null?void 0:c.endsWith(".json"))?e.jsx(q,{traceJson:c}):e.jsx($,{});V.createRoot(document.querySelector("#root")).render(l)})();