@flakiness/sdk 0.129.4 → 0.130.0

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.
@@ -1,7 +1,207 @@
1
1
  // src/localReportApi.ts
2
2
  import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
3
- import fs from "fs";
3
+ import fs2 from "fs";
4
+ import path2 from "path";
4
5
  import { z } from "zod/v4";
6
+
7
+ // src/localGit.ts
8
+ import { exec } from "child_process";
9
+ import { promisify } from "util";
10
+ var execAsync = promisify(exec);
11
+ async function listLocalCommits(gitRoot, head, count) {
12
+ const FIELD_SEPARATOR = "|~|";
13
+ const RECORD_SEPARATOR = "\0";
14
+ const prettyFormat = [
15
+ "%H",
16
+ // %H: Full commit hash
17
+ "%at",
18
+ // %at: Author date as a Unix timestamp (seconds since epoch)
19
+ "%an",
20
+ // %an: Author name
21
+ "%s"
22
+ // %s: Subject (the first line of the commit message)
23
+ ].join(FIELD_SEPARATOR);
24
+ const command = `git log ${head} -n ${count} --pretty=format:"${prettyFormat}" -z`;
25
+ try {
26
+ const { stdout } = await execAsync(command, { cwd: gitRoot });
27
+ if (!stdout) {
28
+ return [];
29
+ }
30
+ return stdout.trim().split(RECORD_SEPARATOR).filter((record) => record).map((record) => {
31
+ const [commitId, timestampStr, author, message] = record.split(FIELD_SEPARATOR);
32
+ return {
33
+ commitId,
34
+ timestamp: parseInt(timestampStr, 10) * 1e3,
35
+ // Convert timestamp from seconds to milliseconds
36
+ author,
37
+ message,
38
+ walkIndex: 0
39
+ };
40
+ });
41
+ } catch (error) {
42
+ console.error(`Failed to list commits for repository at ${gitRoot}:`, error);
43
+ throw error;
44
+ }
45
+ }
46
+
47
+ // src/utils.ts
48
+ import { ReportUtils } from "@flakiness/report";
49
+ import fs from "fs";
50
+ import http from "http";
51
+ import https from "https";
52
+ import path, { posix as posixPath, win32 as win32Path } from "path";
53
+ var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
54
+ function errorText(error) {
55
+ return FLAKINESS_DBG ? error.stack : error.message;
56
+ }
57
+ async function retryWithBackoff(job, backoff = []) {
58
+ for (const timeout of backoff) {
59
+ try {
60
+ return await job();
61
+ } catch (e) {
62
+ if (e instanceof AggregateError)
63
+ console.error(`[flakiness.io err]`, errorText(e.errors[0]));
64
+ else if (e instanceof Error)
65
+ console.error(`[flakiness.io err]`, errorText(e));
66
+ else
67
+ console.error(`[flakiness.io err]`, e);
68
+ await new Promise((x) => setTimeout(x, timeout));
69
+ }
70
+ }
71
+ return await job();
72
+ }
73
+ var httpUtils;
74
+ ((httpUtils2) => {
75
+ function createRequest({ url, method = "get", headers = {} }) {
76
+ let resolve;
77
+ let reject;
78
+ const responseDataPromise = new Promise((a, b) => {
79
+ resolve = a;
80
+ reject = b;
81
+ });
82
+ const protocol = url.startsWith("https") ? https : http;
83
+ headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
84
+ const request = protocol.request(url, { method, headers }, (res) => {
85
+ const chunks = [];
86
+ res.on("data", (chunk) => chunks.push(chunk));
87
+ res.on("end", () => {
88
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
89
+ resolve(Buffer.concat(chunks));
90
+ else
91
+ reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
92
+ });
93
+ res.on("error", (error) => reject(error));
94
+ });
95
+ request.on("error", reject);
96
+ return { request, responseDataPromise };
97
+ }
98
+ httpUtils2.createRequest = createRequest;
99
+ async function getBuffer(url, backoff) {
100
+ return await retryWithBackoff(async () => {
101
+ const { request, responseDataPromise } = createRequest({ url });
102
+ request.end();
103
+ return await responseDataPromise;
104
+ }, backoff);
105
+ }
106
+ httpUtils2.getBuffer = getBuffer;
107
+ async function getText(url, backoff) {
108
+ const buffer = await getBuffer(url, backoff);
109
+ return buffer.toString("utf-8");
110
+ }
111
+ httpUtils2.getText = getText;
112
+ async function getJSON(url) {
113
+ return JSON.parse(await getText(url));
114
+ }
115
+ httpUtils2.getJSON = getJSON;
116
+ async function postText(url, text, backoff) {
117
+ const headers = {
118
+ "Content-Type": "application/json",
119
+ "Content-Length": Buffer.byteLength(text) + ""
120
+ };
121
+ return await retryWithBackoff(async () => {
122
+ const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
123
+ request.write(text);
124
+ request.end();
125
+ return await responseDataPromise;
126
+ }, backoff);
127
+ }
128
+ httpUtils2.postText = postText;
129
+ async function postJSON(url, json, backoff) {
130
+ const buffer = await postText(url, JSON.stringify(json), backoff);
131
+ return JSON.parse(buffer.toString("utf-8"));
132
+ }
133
+ httpUtils2.postJSON = postJSON;
134
+ })(httpUtils || (httpUtils = {}));
135
+ var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
136
+ async function resolveAttachmentPaths(report, attachmentsDir) {
137
+ const attachmentFiles = await listFilesRecursively(attachmentsDir);
138
+ const filenameToPath = new Map(attachmentFiles.map((file) => [path.basename(file), file]));
139
+ const attachmentIdToPath = /* @__PURE__ */ new Map();
140
+ const missingAttachments = /* @__PURE__ */ new Set();
141
+ ReportUtils.visitTests(report, (test) => {
142
+ for (const attempt of test.attempts) {
143
+ for (const attachment of attempt.attachments ?? []) {
144
+ const attachmentPath = filenameToPath.get(attachment.id);
145
+ if (!attachmentPath) {
146
+ missingAttachments.add(attachment.id);
147
+ } else {
148
+ attachmentIdToPath.set(attachment.id, {
149
+ contentType: attachment.contentType,
150
+ id: attachment.id,
151
+ path: attachmentPath
152
+ });
153
+ }
154
+ }
155
+ }
156
+ });
157
+ return { attachmentIdToPath, missingAttachments: Array.from(missingAttachments) };
158
+ }
159
+ async function listFilesRecursively(dir, result = []) {
160
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
161
+ for (const entry of entries) {
162
+ const fullPath = path.join(dir, entry.name);
163
+ if (entry.isDirectory())
164
+ await listFilesRecursively(fullPath, result);
165
+ else
166
+ result.push(fullPath);
167
+ }
168
+ return result;
169
+ }
170
+ var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
171
+ var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
172
+
173
+ // src/localReportApi.ts
174
+ var ReportInfo = class {
175
+ constructor(_options) {
176
+ this._options = _options;
177
+ }
178
+ report;
179
+ attachmentIdToPath = /* @__PURE__ */ new Map();
180
+ commits = [];
181
+ async refresh() {
182
+ const report = await fs2.promises.readFile(this._options.reportPath, "utf-8").then((x) => JSON.parse(x)).catch((e) => void 0);
183
+ if (!report) {
184
+ this.report = void 0;
185
+ this.commits = [];
186
+ this.attachmentIdToPath = /* @__PURE__ */ new Map();
187
+ return;
188
+ }
189
+ if (JSON.stringify(report) === JSON.stringify(this.report))
190
+ return;
191
+ this.report = report;
192
+ this.commits = await listLocalCommits(path2.dirname(this._options.reportPath), report.commitId, 100);
193
+ const attachmentsDir = this._options.attachmentsFolder;
194
+ const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
195
+ if (missingAttachments.length) {
196
+ const first = missingAttachments.slice(0, 3);
197
+ for (let i = 0; i < 3 && i < missingAttachments.length; ++i)
198
+ console.warn(`Missing attachment with id ${missingAttachments[i]}`);
199
+ if (missingAttachments.length > 3)
200
+ console.warn(`...and ${missingAttachments.length - 3} more missing attachments.`);
201
+ }
202
+ this.attachmentIdToPath = attachmentIdToPath;
203
+ }
204
+ };
5
205
  var t = TypedHTTP.Router.create();
6
206
  var localReportRouter = {
7
207
  ping: t.get({
@@ -11,7 +211,7 @@ var localReportRouter = {
11
211
  }),
12
212
  lastCommits: t.get({
13
213
  handler: async ({ ctx }) => {
14
- return ctx.commits;
214
+ return ctx.reportInfo.commits;
15
215
  }
16
216
  }),
17
217
  report: {
@@ -20,21 +220,23 @@ var localReportRouter = {
20
220
  attachmentId: z.string().min(1).max(100).transform((id) => id)
21
221
  }),
22
222
  handler: async ({ ctx, input }) => {
23
- const idx = ctx.attachmentIdToPath.get(input.attachmentId);
223
+ const idx = ctx.reportInfo.attachmentIdToPath.get(input.attachmentId);
24
224
  if (!idx)
25
225
  throw TypedHTTP.HttpError.withCode("NOT_FOUND");
26
- const buffer = await fs.promises.readFile(idx.path);
226
+ const buffer = await fs2.promises.readFile(idx.path);
27
227
  return TypedHTTP.ok(buffer, idx.contentType);
28
228
  }
29
229
  }),
30
230
  json: t.get({
31
231
  handler: async ({ ctx }) => {
32
- return ctx.report;
232
+ await ctx.reportInfo.refresh();
233
+ return ctx.reportInfo.report;
33
234
  }
34
235
  })
35
236
  }
36
237
  };
37
238
  export {
239
+ ReportInfo,
38
240
  localReportRouter
39
241
  };
40
242
  //# sourceMappingURL=localReportApi.js.map
@@ -7,9 +7,14 @@ import compression from "compression";
7
7
  import debug from "debug";
8
8
  import express from "express";
9
9
  import "express-async-errors";
10
- import fs3 from "fs";
11
10
  import http2 from "http";
12
11
 
12
+ // src/localReportApi.ts
13
+ import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
14
+ import fs2 from "fs";
15
+ import path2 from "path";
16
+ import { z } from "zod/v4";
17
+
13
18
  // src/localGit.ts
14
19
  import { exec } from "child_process";
15
20
  import { promisify } from "util";
@@ -50,46 +55,9 @@ async function listLocalCommits(gitRoot, head, count) {
50
55
  }
51
56
  }
52
57
 
53
- // src/localReportApi.ts
54
- import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
55
- import fs from "fs";
56
- import { z } from "zod/v4";
57
- var t = TypedHTTP.Router.create();
58
- var localReportRouter = {
59
- ping: t.get({
60
- handler: async () => {
61
- return "pong";
62
- }
63
- }),
64
- lastCommits: t.get({
65
- handler: async ({ ctx }) => {
66
- return ctx.commits;
67
- }
68
- }),
69
- report: {
70
- attachment: t.rawMethod("GET", {
71
- input: z.object({
72
- attachmentId: z.string().min(1).max(100).transform((id) => id)
73
- }),
74
- handler: async ({ ctx, input }) => {
75
- const idx = ctx.attachmentIdToPath.get(input.attachmentId);
76
- if (!idx)
77
- throw TypedHTTP.HttpError.withCode("NOT_FOUND");
78
- const buffer = await fs.promises.readFile(idx.path);
79
- return TypedHTTP.ok(buffer, idx.contentType);
80
- }
81
- }),
82
- json: t.get({
83
- handler: async ({ ctx }) => {
84
- return ctx.report;
85
- }
86
- })
87
- }
88
- };
89
-
90
58
  // src/utils.ts
91
59
  import { ReportUtils } from "@flakiness/report";
92
- import fs2 from "fs";
60
+ import fs from "fs";
93
61
  import http from "http";
94
62
  import https from "https";
95
63
  import path, { posix as posixPath, win32 as win32Path } from "path";
@@ -200,7 +168,7 @@ async function resolveAttachmentPaths(report, attachmentsDir) {
200
168
  return { attachmentIdToPath, missingAttachments: Array.from(missingAttachments) };
201
169
  }
202
170
  async function listFilesRecursively(dir, result = []) {
203
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
171
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
204
172
  for (const entry of entries) {
205
173
  const fullPath = path.join(dir, entry.name);
206
174
  if (entry.isDirectory())
@@ -213,17 +181,27 @@ async function listFilesRecursively(dir, result = []) {
213
181
  var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
214
182
  var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
215
183
 
216
- // src/localReportServer.ts
217
- var logHTTPServer = debug("fk:http");
218
- var LocalReportServer = class _LocalReportServer {
219
- constructor(_server, _port, _authToken) {
220
- this._server = _server;
221
- this._port = _port;
222
- this._authToken = _authToken;
184
+ // src/localReportApi.ts
185
+ var ReportInfo = class {
186
+ constructor(_options) {
187
+ this._options = _options;
223
188
  }
224
- static async create(options) {
225
- const report = JSON.parse(await fs3.promises.readFile(options.reportPath, "utf-8"));
226
- const attachmentsDir = options.attachmentsFolder;
189
+ report;
190
+ attachmentIdToPath = /* @__PURE__ */ new Map();
191
+ commits = [];
192
+ async refresh() {
193
+ const report = await fs2.promises.readFile(this._options.reportPath, "utf-8").then((x) => JSON.parse(x)).catch((e) => void 0);
194
+ if (!report) {
195
+ this.report = void 0;
196
+ this.commits = [];
197
+ this.attachmentIdToPath = /* @__PURE__ */ new Map();
198
+ return;
199
+ }
200
+ if (JSON.stringify(report) === JSON.stringify(this.report))
201
+ return;
202
+ this.report = report;
203
+ this.commits = await listLocalCommits(path2.dirname(this._options.reportPath), report.commitId, 100);
204
+ const attachmentsDir = this._options.attachmentsFolder;
227
205
  const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
228
206
  if (missingAttachments.length) {
229
207
  const first = missingAttachments.slice(0, 3);
@@ -232,7 +210,52 @@ var LocalReportServer = class _LocalReportServer {
232
210
  if (missingAttachments.length > 3)
233
211
  console.warn(`...and ${missingAttachments.length - 3} more missing attachments.`);
234
212
  }
235
- const commits = await listLocalCommits(process.cwd(), report.commitId, 100);
213
+ this.attachmentIdToPath = attachmentIdToPath;
214
+ }
215
+ };
216
+ var t = TypedHTTP.Router.create();
217
+ var localReportRouter = {
218
+ ping: t.get({
219
+ handler: async () => {
220
+ return "pong";
221
+ }
222
+ }),
223
+ lastCommits: t.get({
224
+ handler: async ({ ctx }) => {
225
+ return ctx.reportInfo.commits;
226
+ }
227
+ }),
228
+ report: {
229
+ attachment: t.rawMethod("GET", {
230
+ input: z.object({
231
+ attachmentId: z.string().min(1).max(100).transform((id) => id)
232
+ }),
233
+ handler: async ({ ctx, input }) => {
234
+ const idx = ctx.reportInfo.attachmentIdToPath.get(input.attachmentId);
235
+ if (!idx)
236
+ throw TypedHTTP.HttpError.withCode("NOT_FOUND");
237
+ const buffer = await fs2.promises.readFile(idx.path);
238
+ return TypedHTTP.ok(buffer, idx.contentType);
239
+ }
240
+ }),
241
+ json: t.get({
242
+ handler: async ({ ctx }) => {
243
+ await ctx.reportInfo.refresh();
244
+ return ctx.reportInfo.report;
245
+ }
246
+ })
247
+ }
248
+ };
249
+
250
+ // src/localReportServer.ts
251
+ var logHTTPServer = debug("fk:http");
252
+ var LocalReportServer = class _LocalReportServer {
253
+ constructor(_server, _port, _authToken) {
254
+ this._server = _server;
255
+ this._port = _port;
256
+ this._authToken = _authToken;
257
+ }
258
+ static async create(options) {
236
259
  const app = express();
237
260
  app.set("etag", false);
238
261
  const authToken = randomUUIDBase62();
@@ -255,9 +278,10 @@ var LocalReportServer = class _LocalReportServer {
255
278
  });
256
279
  next();
257
280
  });
281
+ const reportInfo = new ReportInfo(options);
258
282
  app.use("/" + authToken, createTypedHttpExpressMiddleware({
259
283
  router: localReportRouter,
260
- createRootContext: async ({ req, res, input }) => ({ report, commits, attachmentIdToPath })
284
+ createRootContext: async ({ req, res, input }) => ({ reportInfo })
261
285
  }));
262
286
  app.use((err, req, res, next) => {
263
287
  if (err instanceof TypedHTTP2.HttpError)
@@ -2,13 +2,13 @@
2
2
  import { ReportUtils as ReportUtils2 } from "@flakiness/report";
3
3
  import { Multimap } from "@flakiness/shared/common/multimap.js";
4
4
  import chalk2 from "chalk";
5
- import fs8 from "fs";
6
- import path5 from "path";
5
+ import fs7 from "fs";
6
+ import path6 from "path";
7
7
 
8
8
  // src/cli/cmd-show-report.ts
9
9
  import chalk from "chalk";
10
10
  import open from "open";
11
- import path4 from "path";
11
+ import path5 from "path";
12
12
 
13
13
  // src/flakinessConfig.ts
14
14
  import fs2 from "fs";
@@ -429,9 +429,14 @@ import compression from "compression";
429
429
  import debug from "debug";
430
430
  import express from "express";
431
431
  import "express-async-errors";
432
- import fs5 from "fs";
433
432
  import http2 from "http";
434
433
 
434
+ // src/localReportApi.ts
435
+ import { TypedHTTP as TypedHTTP2 } from "@flakiness/shared/common/typedHttp.js";
436
+ import fs4 from "fs";
437
+ import path4 from "path";
438
+ import { z } from "zod/v4";
439
+
435
440
  // src/localGit.ts
436
441
  import { exec } from "child_process";
437
442
  import { promisify } from "util";
@@ -473,9 +478,37 @@ async function listLocalCommits(gitRoot, head, count) {
473
478
  }
474
479
 
475
480
  // src/localReportApi.ts
476
- import { TypedHTTP as TypedHTTP2 } from "@flakiness/shared/common/typedHttp.js";
477
- import fs4 from "fs";
478
- import { z } from "zod/v4";
481
+ var ReportInfo = class {
482
+ constructor(_options) {
483
+ this._options = _options;
484
+ }
485
+ report;
486
+ attachmentIdToPath = /* @__PURE__ */ new Map();
487
+ commits = [];
488
+ async refresh() {
489
+ const report = await fs4.promises.readFile(this._options.reportPath, "utf-8").then((x) => JSON.parse(x)).catch((e) => void 0);
490
+ if (!report) {
491
+ this.report = void 0;
492
+ this.commits = [];
493
+ this.attachmentIdToPath = /* @__PURE__ */ new Map();
494
+ return;
495
+ }
496
+ if (JSON.stringify(report) === JSON.stringify(this.report))
497
+ return;
498
+ this.report = report;
499
+ this.commits = await listLocalCommits(path4.dirname(this._options.reportPath), report.commitId, 100);
500
+ const attachmentsDir = this._options.attachmentsFolder;
501
+ const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
502
+ if (missingAttachments.length) {
503
+ const first = missingAttachments.slice(0, 3);
504
+ for (let i = 0; i < 3 && i < missingAttachments.length; ++i)
505
+ console.warn(`Missing attachment with id ${missingAttachments[i]}`);
506
+ if (missingAttachments.length > 3)
507
+ console.warn(`...and ${missingAttachments.length - 3} more missing attachments.`);
508
+ }
509
+ this.attachmentIdToPath = attachmentIdToPath;
510
+ }
511
+ };
479
512
  var t = TypedHTTP2.Router.create();
480
513
  var localReportRouter = {
481
514
  ping: t.get({
@@ -485,7 +518,7 @@ var localReportRouter = {
485
518
  }),
486
519
  lastCommits: t.get({
487
520
  handler: async ({ ctx }) => {
488
- return ctx.commits;
521
+ return ctx.reportInfo.commits;
489
522
  }
490
523
  }),
491
524
  report: {
@@ -494,7 +527,7 @@ var localReportRouter = {
494
527
  attachmentId: z.string().min(1).max(100).transform((id) => id)
495
528
  }),
496
529
  handler: async ({ ctx, input }) => {
497
- const idx = ctx.attachmentIdToPath.get(input.attachmentId);
530
+ const idx = ctx.reportInfo.attachmentIdToPath.get(input.attachmentId);
498
531
  if (!idx)
499
532
  throw TypedHTTP2.HttpError.withCode("NOT_FOUND");
500
533
  const buffer = await fs4.promises.readFile(idx.path);
@@ -503,7 +536,8 @@ var localReportRouter = {
503
536
  }),
504
537
  json: t.get({
505
538
  handler: async ({ ctx }) => {
506
- return ctx.report;
539
+ await ctx.reportInfo.refresh();
540
+ return ctx.reportInfo.report;
507
541
  }
508
542
  })
509
543
  }
@@ -518,17 +552,6 @@ var LocalReportServer = class _LocalReportServer {
518
552
  this._authToken = _authToken;
519
553
  }
520
554
  static async create(options) {
521
- const report = JSON.parse(await fs5.promises.readFile(options.reportPath, "utf-8"));
522
- const attachmentsDir = options.attachmentsFolder;
523
- const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
524
- if (missingAttachments.length) {
525
- const first = missingAttachments.slice(0, 3);
526
- for (let i = 0; i < 3 && i < missingAttachments.length; ++i)
527
- console.warn(`Missing attachment with id ${missingAttachments[i]}`);
528
- if (missingAttachments.length > 3)
529
- console.warn(`...and ${missingAttachments.length - 3} more missing attachments.`);
530
- }
531
- const commits = await listLocalCommits(process.cwd(), report.commitId, 100);
532
555
  const app = express();
533
556
  app.set("etag", false);
534
557
  const authToken = randomUUIDBase62();
@@ -551,9 +574,10 @@ var LocalReportServer = class _LocalReportServer {
551
574
  });
552
575
  next();
553
576
  });
577
+ const reportInfo = new ReportInfo(options);
554
578
  app.use("/" + authToken, createTypedHttpExpressMiddleware({
555
579
  router: localReportRouter,
556
- createRootContext: async ({ req, res, input }) => ({ report, commits, attachmentIdToPath })
580
+ createRootContext: async ({ req, res, input }) => ({ reportInfo })
557
581
  }));
558
582
  app.use((err2, req, res, next) => {
559
583
  if (err2 instanceof TypedHTTP3.HttpError)
@@ -587,7 +611,7 @@ var LocalReportServer = class _LocalReportServer {
587
611
 
588
612
  // src/cli/cmd-show-report.ts
589
613
  async function cmdShowReport(reportFolder) {
590
- const reportPath = path4.join(reportFolder, "report.json");
614
+ const reportPath = path5.join(reportFolder, "report.json");
591
615
  const session = await FlakinessSession.load();
592
616
  const config = await FlakinessConfig.load();
593
617
  const projectPublicId = config.projectPublicId();
@@ -610,12 +634,12 @@ async function cmdShowReport(reportFolder) {
610
634
 
611
635
  // src/createTestStepSnippets.ts
612
636
  import { codeFrameColumns } from "@babel/code-frame";
613
- import fs6 from "fs";
637
+ import fs5 from "fs";
614
638
  function createTestStepSnippets(filepathToSteps) {
615
639
  for (const [filepath, steps] of filepathToSteps) {
616
640
  let source;
617
641
  try {
618
- source = fs6.readFileSync(filepath, "utf-8");
642
+ source = fs5.readFileSync(filepath, "utf-8");
619
643
  } catch (e) {
620
644
  continue;
621
645
  }
@@ -640,7 +664,7 @@ function createTestStepSnippets(filepathToSteps) {
640
664
  // src/reportUploader.ts
641
665
  import { compressTextAsync, compressTextSync } from "@flakiness/shared/node/compression.js";
642
666
  import assert2 from "assert";
643
- import fs7 from "fs";
667
+ import fs6 from "fs";
644
668
  import { URL } from "url";
645
669
  var ReportUploader = class _ReportUploader {
646
670
  static optionsFromEnv(overrides) {
@@ -738,16 +762,16 @@ var ReportUpload = class {
738
762
  url: uploadUrl,
739
763
  headers: {
740
764
  "Content-Type": attachment.contentType,
741
- "Content-Length": (await fs7.promises.stat(attachmentPath)).size + ""
765
+ "Content-Length": (await fs6.promises.stat(attachmentPath)).size + ""
742
766
  },
743
767
  method: "put"
744
768
  });
745
- fs7.createReadStream(attachmentPath).pipe(request);
769
+ fs6.createReadStream(attachmentPath).pipe(request);
746
770
  await responseDataPromise;
747
771
  }, HTTP_BACKOFF);
748
772
  return;
749
773
  }
750
- let buffer = attachment.body ? attachment.body : attachment.path ? await fs7.promises.readFile(attachment.path) : void 0;
774
+ let buffer = attachment.body ? attachment.body : attachment.path ? await fs6.promises.readFile(attachment.path) : void 0;
751
775
  assert2(buffer);
752
776
  const encoding = compressable ? "br" : void 0;
753
777
  if (compressable)
@@ -844,7 +868,7 @@ var err = (txt) => console.error(chalk2.red(`[flakiness.io] ${txt}`));
844
868
  var FlakinessReporter = class {
845
869
  constructor(_options = {}) {
846
870
  this._options = _options;
847
- this._outputFolder = path5.join(process.cwd(), this._options.outputFolder ?? process.env.FLAKINESS_OUTPUT_DIR ?? "flakiness-report");
871
+ this._outputFolder = path6.join(process.cwd(), this._options.outputFolder ?? process.env.FLAKINESS_OUTPUT_DIR ?? "flakiness-report");
848
872
  }
849
873
  _config;
850
874
  _rootSuite;
@@ -947,7 +971,7 @@ var FlakinessReporter = class {
947
971
  location: pwStep.location ? this._createLocation(context, pwStep.location) : void 0
948
972
  };
949
973
  if (pwStep.location) {
950
- const resolvedPath = path5.resolve(pwStep.location.file);
974
+ const resolvedPath = path6.resolve(pwStep.location.file);
951
975
  this._filepathToSteps.set(resolvedPath, step);
952
976
  }
953
977
  if (pwStep.error)
@@ -996,10 +1020,10 @@ var FlakinessReporter = class {
996
1020
  const environmentsMap = createEnvironments(this._config.projects);
997
1021
  if (this._options.collectBrowserVersions) {
998
1022
  try {
999
- let playwrightPath = fs8.realpathSync(process.argv[1]);
1000
- while (path5.basename(playwrightPath) !== "test")
1001
- playwrightPath = path5.dirname(playwrightPath);
1002
- const module = await import(path5.join(playwrightPath, "index.js"));
1023
+ let playwrightPath = fs7.realpathSync(process.argv[1]);
1024
+ while (path6.basename(playwrightPath) !== "test")
1025
+ playwrightPath = path6.dirname(playwrightPath);
1026
+ const module = await import(path6.join(playwrightPath, "index.js"));
1003
1027
  for (const [project, env] of environmentsMap) {
1004
1028
  const { browserName = "chromium", channel, headless } = project.use;
1005
1029
  let browserType;
@@ -1048,19 +1072,19 @@ var FlakinessReporter = class {
1048
1072
  for (const unaccessibleAttachment of context.unaccessibleAttachmentPaths)
1049
1073
  warn(`cannot access attachment ${unaccessibleAttachment}`);
1050
1074
  this._report = report;
1051
- const reportPath = path5.join(this._outputFolder, "report.json");
1052
- const attachmentsFolder = path5.join(this._outputFolder, "attachments");
1053
- await fs8.promises.rm(this._outputFolder, { recursive: true, force: true });
1054
- await fs8.promises.mkdir(this._outputFolder, { recursive: true });
1055
- await fs8.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
1075
+ const reportPath = path6.join(this._outputFolder, "report.json");
1076
+ const attachmentsFolder = path6.join(this._outputFolder, "attachments");
1077
+ await fs7.promises.rm(this._outputFolder, { recursive: true, force: true });
1078
+ await fs7.promises.mkdir(this._outputFolder, { recursive: true });
1079
+ await fs7.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
1056
1080
  if (context.attachments.size)
1057
- await fs8.promises.mkdir(attachmentsFolder);
1081
+ await fs7.promises.mkdir(attachmentsFolder);
1058
1082
  for (const attachment of context.attachments.values()) {
1059
- const attachmentPath = path5.join(attachmentsFolder, attachment.id);
1083
+ const attachmentPath = path6.join(attachmentsFolder, attachment.id);
1060
1084
  if (attachment.path)
1061
- await fs8.promises.cp(attachment.path, attachmentPath);
1085
+ await fs7.promises.cp(attachment.path, attachmentPath);
1062
1086
  else if (attachment.body)
1063
- await fs8.promises.writeFile(attachmentPath, attachment.body);
1087
+ await fs7.promises.writeFile(attachmentPath, attachment.body);
1064
1088
  this._attachments.push({
1065
1089
  contentType: attachment.contentType,
1066
1090
  id: attachment.id,
@@ -1084,8 +1108,8 @@ var FlakinessReporter = class {
1084
1108
  if (shouldOpen) {
1085
1109
  await cmdShowReport(this._outputFolder);
1086
1110
  } else {
1087
- const defaultOutputFolder = path5.join(process.cwd(), "flakiness-report");
1088
- const folder = defaultOutputFolder === this._outputFolder ? "" : path5.relative(process.cwd(), this._outputFolder);
1111
+ const defaultOutputFolder = path6.join(process.cwd(), "flakiness-report");
1112
+ const folder = defaultOutputFolder === this._outputFolder ? "" : path6.relative(process.cwd(), this._outputFolder);
1089
1113
  console.log(`
1090
1114
  To open last Flakiness report run:
1091
1115