@checkly/playwright-reporter 0.1.10 → 1.2.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.
package/dist/index.js CHANGED
@@ -1,6 +1,57 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ MissingCredentialsError: () => MissingCredentialsError,
34
+ createChecklyReporter: () => createChecklyReporter,
35
+ default: () => index_default
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/extensions/checkly-upload.ts
40
+ var fs5 = __toESM(require("fs"));
41
+ var path4 = __toESM(require("path"));
42
+
43
+ // ../utils/src/ansi.ts
44
+ var ansiRegex = new RegExp(
45
+ [
46
+ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
47
+ "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"
48
+ ].join("|"),
49
+ "g"
50
+ );
51
+
1
52
  // ../utils/src/asset-collector.ts
2
- import * as fs from "fs";
3
- import * as path from "path";
53
+ var fs = __toESM(require("fs"), 1);
54
+ var path = __toESM(require("path"), 1);
4
55
  var AssetCollector = class {
5
56
  constructor(testResultsDir) {
6
57
  this.testResultsDir = testResultsDir;
@@ -234,8 +285,37 @@ var AssetCollector = class {
234
285
  }
235
286
  };
236
287
 
288
+ // ../utils/src/ci-detector.ts
289
+ function detectCI() {
290
+ if (process.env.GITHUB_ACTIONS === "true") {
291
+ return { environment: "ci", ciProvider: "github-actions", repositoryId: process.env.GITHUB_REPOSITORY };
292
+ }
293
+ if (process.env.GITLAB_CI === "true") {
294
+ return { environment: "ci", ciProvider: "gitlab-ci", repositoryId: process.env.CI_PROJECT_PATH };
295
+ }
296
+ if (process.env.JENKINS_URL) {
297
+ return { environment: "ci", ciProvider: "jenkins", repositoryId: process.env.JOB_NAME };
298
+ }
299
+ if (process.env.CIRCLECI === "true") {
300
+ return { environment: "ci", ciProvider: "circleci", repositoryId: process.env.CIRCLE_PROJECT_REPONAME };
301
+ }
302
+ if (process.env.TRAVIS === "true") {
303
+ return { environment: "ci", ciProvider: "travis-ci", repositoryId: process.env.TRAVIS_REPO_SLUG };
304
+ }
305
+ if (process.env.TF_BUILD === "True") {
306
+ return { environment: "ci", ciProvider: "azure-devops", repositoryId: process.env.BUILD_REPOSITORY_NAME };
307
+ }
308
+ if (process.env.BITBUCKET_PIPELINE_UUID) {
309
+ return { environment: "ci", ciProvider: "bitbucket-pipelines", repositoryId: process.env.BITBUCKET_REPO_SLUG };
310
+ }
311
+ if (process.env.CI === "true" || process.env.CI === "1") {
312
+ return { environment: "ci", ciProvider: "unknown-ci" };
313
+ }
314
+ return { environment: "local", ciProvider: "local" };
315
+ }
316
+
237
317
  // ../utils/src/console-adapter.ts
238
- import { createHash } from "crypto";
318
+ var import_node_crypto = require("crypto");
239
319
  function normalizeType(messageType) {
240
320
  switch (messageType.toLowerCase()) {
241
321
  case "debug":
@@ -252,7 +332,7 @@ function normalizeType(messageType) {
252
332
  }
253
333
  }
254
334
  function generateId(time, messageType, text, url) {
255
- return createHash("sha256").update(`${time}-${messageType}-${text}-${url}`).digest("hex").substring(0, 16);
335
+ return (0, import_node_crypto.createHash)("sha256").update(`${time}-${messageType}-${text}-${url}`).digest("hex").substring(0, 16);
256
336
  }
257
337
  function toConsoleMessage(event) {
258
338
  const url = event.location?.url || "";
@@ -269,10 +349,29 @@ function toConsoleMessage(event) {
269
349
  };
270
350
  }
271
351
 
352
+ // ../utils/src/git-info.ts
353
+ function getGitHubRepoInfo() {
354
+ const repository = process.env.GITHUB_REPOSITORY;
355
+ if (!repository) return void 0;
356
+ return {
357
+ repoUrl: `https://github.com/${repository}`,
358
+ commitId: process.env.GITHUB_SHA,
359
+ branchName: process.env.GITHUB_REF_NAME,
360
+ commitOwner: process.env.GITHUB_ACTOR,
361
+ commitMessage: process.env.GITHUB_EVENT_NAME
362
+ };
363
+ }
364
+
365
+ // ../utils/src/machine-id.ts
366
+ var import_node_crypto2 = require("crypto");
367
+ var fs2 = __toESM(require("fs"), 1);
368
+ var os = __toESM(require("os"), 1);
369
+ var path2 = __toESM(require("path"), 1);
370
+
272
371
  // ../utils/src/network-adapter.ts
273
- import { createHash as createHash2 } from "crypto";
372
+ var import_node_crypto3 = require("crypto");
274
373
  function generateId2(url, method, startedAt) {
275
- return createHash2("sha256").update(`${url}-${method}-${startedAt}`).digest("hex").substring(0, 16);
374
+ return (0, import_node_crypto3.createHash)("sha256").update(`${url}-${method}-${startedAt}`).digest("hex").substring(0, 16);
276
375
  }
277
376
  function extractDomain(url) {
278
377
  try {
@@ -330,13 +429,13 @@ function toNetworkRequest(event) {
330
429
  }
331
430
 
332
431
  // ../utils/src/trace-reader.ts
333
- import * as fs2 from "fs";
432
+ var fs3 = __toESM(require("fs"), 1);
334
433
 
335
434
  // ../utils/src/zip-reader.ts
336
- import { promisify } from "util";
337
- import * as zlib from "zlib";
338
- var gunzip2 = promisify(zlib.gunzip);
339
- var inflateRaw2 = promisify(zlib.inflateRaw);
435
+ var import_node_util = require("util");
436
+ var zlib = __toESM(require("zlib"), 1);
437
+ var gunzip2 = (0, import_node_util.promisify)(zlib.gunzip);
438
+ var inflateRaw2 = (0, import_node_util.promisify)(zlib.inflateRaw);
340
439
  function parseZipEntries(zipBuffer) {
341
440
  const EOCD_SIG = 101010256;
342
441
  let eocdOffset = -1;
@@ -407,11 +506,11 @@ var TraceReader = class {
407
506
  zipBuffer = null;
408
507
  traceEntries = [];
409
508
  async open() {
410
- if (!fs2.existsSync(this.tracePath)) {
509
+ if (!fs3.existsSync(this.tracePath)) {
411
510
  return false;
412
511
  }
413
512
  try {
414
- this.zipBuffer = fs2.readFileSync(this.tracePath);
513
+ this.zipBuffer = fs3.readFileSync(this.tracePath);
415
514
  const entries = parseZipEntries(this.zipBuffer);
416
515
  this.traceEntries = entries.filter(
417
516
  (e) => (/^\d+-trace\.trace$/.test(e.fileName) || /^\d+-trace\.network$/.test(e.fileName)) && !e.fileName.includes("/")
@@ -488,14 +587,732 @@ var TraceReader = class {
488
587
  };
489
588
 
490
589
  // ../utils/src/zipper.ts
491
- import * as fs3 from "fs";
492
- import * as os from "os";
493
- import * as path2 from "path";
494
- import { ZipArchive } from "archiver";
590
+ var fs4 = __toESM(require("fs"), 1);
591
+ var os2 = __toESM(require("os"), 1);
592
+ var path3 = __toESM(require("path"), 1);
593
+
594
+ // ../../node_modules/.pnpm/fflate@0.8.2/node_modules/fflate/esm/index.mjs
595
+ var import_module = require("module");
596
+ var require2 = (0, import_module.createRequire)("/");
597
+ var Worker;
598
+ try {
599
+ Worker = require2("worker_threads").Worker;
600
+ } catch (e) {
601
+ }
602
+ var u8 = Uint8Array;
603
+ var u16 = Uint16Array;
604
+ var i32 = Int32Array;
605
+ var fleb = new u8([
606
+ 0,
607
+ 0,
608
+ 0,
609
+ 0,
610
+ 0,
611
+ 0,
612
+ 0,
613
+ 0,
614
+ 1,
615
+ 1,
616
+ 1,
617
+ 1,
618
+ 2,
619
+ 2,
620
+ 2,
621
+ 2,
622
+ 3,
623
+ 3,
624
+ 3,
625
+ 3,
626
+ 4,
627
+ 4,
628
+ 4,
629
+ 4,
630
+ 5,
631
+ 5,
632
+ 5,
633
+ 5,
634
+ 0,
635
+ /* unused */
636
+ 0,
637
+ 0,
638
+ /* impossible */
639
+ 0
640
+ ]);
641
+ var fdeb = new u8([
642
+ 0,
643
+ 0,
644
+ 0,
645
+ 0,
646
+ 1,
647
+ 1,
648
+ 2,
649
+ 2,
650
+ 3,
651
+ 3,
652
+ 4,
653
+ 4,
654
+ 5,
655
+ 5,
656
+ 6,
657
+ 6,
658
+ 7,
659
+ 7,
660
+ 8,
661
+ 8,
662
+ 9,
663
+ 9,
664
+ 10,
665
+ 10,
666
+ 11,
667
+ 11,
668
+ 12,
669
+ 12,
670
+ 13,
671
+ 13,
672
+ /* unused */
673
+ 0,
674
+ 0
675
+ ]);
676
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
677
+ var freb = function(eb, start) {
678
+ var b = new u16(31);
679
+ for (var i = 0; i < 31; ++i) {
680
+ b[i] = start += 1 << eb[i - 1];
681
+ }
682
+ var r = new i32(b[30]);
683
+ for (var i = 1; i < 30; ++i) {
684
+ for (var j = b[i]; j < b[i + 1]; ++j) {
685
+ r[j] = j - b[i] << 5 | i;
686
+ }
687
+ }
688
+ return { b, r };
689
+ };
690
+ var _a = freb(fleb, 2);
691
+ var fl = _a.b;
692
+ var revfl = _a.r;
693
+ fl[28] = 258, revfl[258] = 28;
694
+ var _b = freb(fdeb, 0);
695
+ var fd = _b.b;
696
+ var revfd = _b.r;
697
+ var rev = new u16(32768);
698
+ for (i = 0; i < 32768; ++i) {
699
+ x = (i & 43690) >> 1 | (i & 21845) << 1;
700
+ x = (x & 52428) >> 2 | (x & 13107) << 2;
701
+ x = (x & 61680) >> 4 | (x & 3855) << 4;
702
+ rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
703
+ }
704
+ var x;
705
+ var i;
706
+ var hMap = (function(cd, mb, r) {
707
+ var s = cd.length;
708
+ var i = 0;
709
+ var l = new u16(mb);
710
+ for (; i < s; ++i) {
711
+ if (cd[i])
712
+ ++l[cd[i] - 1];
713
+ }
714
+ var le = new u16(mb);
715
+ for (i = 1; i < mb; ++i) {
716
+ le[i] = le[i - 1] + l[i - 1] << 1;
717
+ }
718
+ var co;
719
+ if (r) {
720
+ co = new u16(1 << mb);
721
+ var rvb = 15 - mb;
722
+ for (i = 0; i < s; ++i) {
723
+ if (cd[i]) {
724
+ var sv = i << 4 | cd[i];
725
+ var r_1 = mb - cd[i];
726
+ var v = le[cd[i] - 1]++ << r_1;
727
+ for (var m = v | (1 << r_1) - 1; v <= m; ++v) {
728
+ co[rev[v] >> rvb] = sv;
729
+ }
730
+ }
731
+ }
732
+ } else {
733
+ co = new u16(s);
734
+ for (i = 0; i < s; ++i) {
735
+ if (cd[i]) {
736
+ co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
737
+ }
738
+ }
739
+ }
740
+ return co;
741
+ });
742
+ var flt = new u8(288);
743
+ for (i = 0; i < 144; ++i)
744
+ flt[i] = 8;
745
+ var i;
746
+ for (i = 144; i < 256; ++i)
747
+ flt[i] = 9;
748
+ var i;
749
+ for (i = 256; i < 280; ++i)
750
+ flt[i] = 7;
751
+ var i;
752
+ for (i = 280; i < 288; ++i)
753
+ flt[i] = 8;
754
+ var i;
755
+ var fdt = new u8(32);
756
+ for (i = 0; i < 32; ++i)
757
+ fdt[i] = 5;
758
+ var i;
759
+ var flm = /* @__PURE__ */ hMap(flt, 9, 0);
760
+ var fdm = /* @__PURE__ */ hMap(fdt, 5, 0);
761
+ var shft = function(p) {
762
+ return (p + 7) / 8 | 0;
763
+ };
764
+ var slc = function(v, s, e) {
765
+ if (s == null || s < 0)
766
+ s = 0;
767
+ if (e == null || e > v.length)
768
+ e = v.length;
769
+ return new u8(v.subarray(s, e));
770
+ };
771
+ var ec = [
772
+ "unexpected EOF",
773
+ "invalid block type",
774
+ "invalid length/literal",
775
+ "invalid distance",
776
+ "stream finished",
777
+ "no stream handler",
778
+ ,
779
+ "no callback",
780
+ "invalid UTF-8 data",
781
+ "extra field too long",
782
+ "date not in range 1980-2099",
783
+ "filename too long",
784
+ "stream finishing",
785
+ "invalid zip data"
786
+ // determined by unknown compression method
787
+ ];
788
+ var err = function(ind, msg, nt) {
789
+ var e = new Error(msg || ec[ind]);
790
+ e.code = ind;
791
+ if (Error.captureStackTrace)
792
+ Error.captureStackTrace(e, err);
793
+ if (!nt)
794
+ throw e;
795
+ return e;
796
+ };
797
+ var wbits = function(d, p, v) {
798
+ v <<= p & 7;
799
+ var o = p / 8 | 0;
800
+ d[o] |= v;
801
+ d[o + 1] |= v >> 8;
802
+ };
803
+ var wbits16 = function(d, p, v) {
804
+ v <<= p & 7;
805
+ var o = p / 8 | 0;
806
+ d[o] |= v;
807
+ d[o + 1] |= v >> 8;
808
+ d[o + 2] |= v >> 16;
809
+ };
810
+ var hTree = function(d, mb) {
811
+ var t = [];
812
+ for (var i = 0; i < d.length; ++i) {
813
+ if (d[i])
814
+ t.push({ s: i, f: d[i] });
815
+ }
816
+ var s = t.length;
817
+ var t2 = t.slice();
818
+ if (!s)
819
+ return { t: et, l: 0 };
820
+ if (s == 1) {
821
+ var v = new u8(t[0].s + 1);
822
+ v[t[0].s] = 1;
823
+ return { t: v, l: 1 };
824
+ }
825
+ t.sort(function(a, b) {
826
+ return a.f - b.f;
827
+ });
828
+ t.push({ s: -1, f: 25001 });
829
+ var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
830
+ t[0] = { s: -1, f: l.f + r.f, l, r };
831
+ while (i1 != s - 1) {
832
+ l = t[t[i0].f < t[i2].f ? i0++ : i2++];
833
+ r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
834
+ t[i1++] = { s: -1, f: l.f + r.f, l, r };
835
+ }
836
+ var maxSym = t2[0].s;
837
+ for (var i = 1; i < s; ++i) {
838
+ if (t2[i].s > maxSym)
839
+ maxSym = t2[i].s;
840
+ }
841
+ var tr = new u16(maxSym + 1);
842
+ var mbt = ln(t[i1 - 1], tr, 0);
843
+ if (mbt > mb) {
844
+ var i = 0, dt = 0;
845
+ var lft = mbt - mb, cst = 1 << lft;
846
+ t2.sort(function(a, b) {
847
+ return tr[b.s] - tr[a.s] || a.f - b.f;
848
+ });
849
+ for (; i < s; ++i) {
850
+ var i2_1 = t2[i].s;
851
+ if (tr[i2_1] > mb) {
852
+ dt += cst - (1 << mbt - tr[i2_1]);
853
+ tr[i2_1] = mb;
854
+ } else
855
+ break;
856
+ }
857
+ dt >>= lft;
858
+ while (dt > 0) {
859
+ var i2_2 = t2[i].s;
860
+ if (tr[i2_2] < mb)
861
+ dt -= 1 << mb - tr[i2_2]++ - 1;
862
+ else
863
+ ++i;
864
+ }
865
+ for (; i >= 0 && dt; --i) {
866
+ var i2_3 = t2[i].s;
867
+ if (tr[i2_3] == mb) {
868
+ --tr[i2_3];
869
+ ++dt;
870
+ }
871
+ }
872
+ mbt = mb;
873
+ }
874
+ return { t: new u8(tr), l: mbt };
875
+ };
876
+ var ln = function(n, l, d) {
877
+ return n.s == -1 ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1)) : l[n.s] = d;
878
+ };
879
+ var lc = function(c) {
880
+ var s = c.length;
881
+ while (s && !c[--s])
882
+ ;
883
+ var cl = new u16(++s);
884
+ var cli = 0, cln = c[0], cls = 1;
885
+ var w = function(v) {
886
+ cl[cli++] = v;
887
+ };
888
+ for (var i = 1; i <= s; ++i) {
889
+ if (c[i] == cln && i != s)
890
+ ++cls;
891
+ else {
892
+ if (!cln && cls > 2) {
893
+ for (; cls > 138; cls -= 138)
894
+ w(32754);
895
+ if (cls > 2) {
896
+ w(cls > 10 ? cls - 11 << 5 | 28690 : cls - 3 << 5 | 12305);
897
+ cls = 0;
898
+ }
899
+ } else if (cls > 3) {
900
+ w(cln), --cls;
901
+ for (; cls > 6; cls -= 6)
902
+ w(8304);
903
+ if (cls > 2)
904
+ w(cls - 3 << 5 | 8208), cls = 0;
905
+ }
906
+ while (cls--)
907
+ w(cln);
908
+ cls = 1;
909
+ cln = c[i];
910
+ }
911
+ }
912
+ return { c: cl.subarray(0, cli), n: s };
913
+ };
914
+ var clen = function(cf, cl) {
915
+ var l = 0;
916
+ for (var i = 0; i < cl.length; ++i)
917
+ l += cf[i] * cl[i];
918
+ return l;
919
+ };
920
+ var wfblk = function(out, pos, dat) {
921
+ var s = dat.length;
922
+ var o = shft(pos + 2);
923
+ out[o] = s & 255;
924
+ out[o + 1] = s >> 8;
925
+ out[o + 2] = out[o] ^ 255;
926
+ out[o + 3] = out[o + 1] ^ 255;
927
+ for (var i = 0; i < s; ++i)
928
+ out[o + i + 4] = dat[i];
929
+ return (o + 4 + s) * 8;
930
+ };
931
+ var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
932
+ wbits(out, p++, final);
933
+ ++lf[256];
934
+ var _a2 = hTree(lf, 15), dlt = _a2.t, mlb = _a2.l;
935
+ var _b2 = hTree(df, 15), ddt = _b2.t, mdb = _b2.l;
936
+ var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
937
+ var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
938
+ var lcfreq = new u16(19);
939
+ for (var i = 0; i < lclt.length; ++i)
940
+ ++lcfreq[lclt[i] & 31];
941
+ for (var i = 0; i < lcdt.length; ++i)
942
+ ++lcfreq[lcdt[i] & 31];
943
+ var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
944
+ var nlcc = 19;
945
+ for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
946
+ ;
947
+ var flen = bl + 5 << 3;
948
+ var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
949
+ var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
950
+ if (bs >= 0 && flen <= ftlen && flen <= dtlen)
951
+ return wfblk(out, p, dat.subarray(bs, bs + bl));
952
+ var lm, ll, dm, dl;
953
+ wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
954
+ if (dtlen < ftlen) {
955
+ lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
956
+ var llm = hMap(lct, mlcb, 0);
957
+ wbits(out, p, nlc - 257);
958
+ wbits(out, p + 5, ndc - 1);
959
+ wbits(out, p + 10, nlcc - 4);
960
+ p += 14;
961
+ for (var i = 0; i < nlcc; ++i)
962
+ wbits(out, p + 3 * i, lct[clim[i]]);
963
+ p += 3 * nlcc;
964
+ var lcts = [lclt, lcdt];
965
+ for (var it = 0; it < 2; ++it) {
966
+ var clct = lcts[it];
967
+ for (var i = 0; i < clct.length; ++i) {
968
+ var len = clct[i] & 31;
969
+ wbits(out, p, llm[len]), p += lct[len];
970
+ if (len > 15)
971
+ wbits(out, p, clct[i] >> 5 & 127), p += clct[i] >> 12;
972
+ }
973
+ }
974
+ } else {
975
+ lm = flm, ll = flt, dm = fdm, dl = fdt;
976
+ }
977
+ for (var i = 0; i < li; ++i) {
978
+ var sym = syms[i];
979
+ if (sym > 255) {
980
+ var len = sym >> 18 & 31;
981
+ wbits16(out, p, lm[len + 257]), p += ll[len + 257];
982
+ if (len > 7)
983
+ wbits(out, p, sym >> 23 & 31), p += fleb[len];
984
+ var dst = sym & 31;
985
+ wbits16(out, p, dm[dst]), p += dl[dst];
986
+ if (dst > 3)
987
+ wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
988
+ } else {
989
+ wbits16(out, p, lm[sym]), p += ll[sym];
990
+ }
991
+ }
992
+ wbits16(out, p, lm[256]);
993
+ return p + ll[256];
994
+ };
995
+ var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
996
+ var et = /* @__PURE__ */ new u8(0);
997
+ var dflt = function(dat, lvl, plvl, pre, post, st) {
998
+ var s = st.z || dat.length;
999
+ var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
1000
+ var w = o.subarray(pre, o.length - post);
1001
+ var lst = st.l;
1002
+ var pos = (st.r || 0) & 7;
1003
+ if (lvl) {
1004
+ if (pos)
1005
+ w[0] = st.r >> 3;
1006
+ var opt = deo[lvl - 1];
1007
+ var n = opt >> 13, c = opt & 8191;
1008
+ var msk_1 = (1 << plvl) - 1;
1009
+ var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
1010
+ var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
1011
+ var hsh = function(i2) {
1012
+ return (dat[i2] ^ dat[i2 + 1] << bs1_1 ^ dat[i2 + 2] << bs2_1) & msk_1;
1013
+ };
1014
+ var syms = new i32(25e3);
1015
+ var lf = new u16(288), df = new u16(32);
1016
+ var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
1017
+ for (; i + 2 < s; ++i) {
1018
+ var hv = hsh(i);
1019
+ var imod = i & 32767, pimod = head[hv];
1020
+ prev[imod] = pimod;
1021
+ head[hv] = imod;
1022
+ if (wi <= i) {
1023
+ var rem = s - i;
1024
+ if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
1025
+ pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);
1026
+ li = lc_1 = eb = 0, bs = i;
1027
+ for (var j = 0; j < 286; ++j)
1028
+ lf[j] = 0;
1029
+ for (var j = 0; j < 30; ++j)
1030
+ df[j] = 0;
1031
+ }
1032
+ var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
1033
+ if (rem > 2 && hv == hsh(i - dif)) {
1034
+ var maxn = Math.min(n, rem) - 1;
1035
+ var maxd = Math.min(32767, i);
1036
+ var ml = Math.min(258, rem);
1037
+ while (dif <= maxd && --ch_1 && imod != pimod) {
1038
+ if (dat[i + l] == dat[i + l - dif]) {
1039
+ var nl = 0;
1040
+ for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)
1041
+ ;
1042
+ if (nl > l) {
1043
+ l = nl, d = dif;
1044
+ if (nl > maxn)
1045
+ break;
1046
+ var mmd = Math.min(dif, nl - 2);
1047
+ var md = 0;
1048
+ for (var j = 0; j < mmd; ++j) {
1049
+ var ti = i - dif + j & 32767;
1050
+ var pti = prev[ti];
1051
+ var cd = ti - pti & 32767;
1052
+ if (cd > md)
1053
+ md = cd, pimod = ti;
1054
+ }
1055
+ }
1056
+ }
1057
+ imod = pimod, pimod = prev[imod];
1058
+ dif += imod - pimod & 32767;
1059
+ }
1060
+ }
1061
+ if (d) {
1062
+ syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
1063
+ var lin = revfl[l] & 31, din = revfd[d] & 31;
1064
+ eb += fleb[lin] + fdeb[din];
1065
+ ++lf[257 + lin];
1066
+ ++df[din];
1067
+ wi = i + l;
1068
+ ++lc_1;
1069
+ } else {
1070
+ syms[li++] = dat[i];
1071
+ ++lf[dat[i]];
1072
+ }
1073
+ }
1074
+ }
1075
+ for (i = Math.max(i, wi); i < s; ++i) {
1076
+ syms[li++] = dat[i];
1077
+ ++lf[dat[i]];
1078
+ }
1079
+ pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
1080
+ if (!lst) {
1081
+ st.r = pos & 7 | w[pos / 8 | 0] << 3;
1082
+ pos -= 7;
1083
+ st.h = head, st.p = prev, st.i = i, st.w = wi;
1084
+ }
1085
+ } else {
1086
+ for (var i = st.w || 0; i < s + lst; i += 65535) {
1087
+ var e = i + 65535;
1088
+ if (e >= s) {
1089
+ w[pos / 8 | 0] = lst;
1090
+ e = s;
1091
+ }
1092
+ pos = wfblk(w, pos + 1, dat.subarray(i, e));
1093
+ }
1094
+ st.i = s;
1095
+ }
1096
+ return slc(o, 0, pre + shft(pos) + post);
1097
+ };
1098
+ var crct = /* @__PURE__ */ (function() {
1099
+ var t = new Int32Array(256);
1100
+ for (var i = 0; i < 256; ++i) {
1101
+ var c = i, k = 9;
1102
+ while (--k)
1103
+ c = (c & 1 && -306674912) ^ c >>> 1;
1104
+ t[i] = c;
1105
+ }
1106
+ return t;
1107
+ })();
1108
+ var crc = function() {
1109
+ var c = -1;
1110
+ return {
1111
+ p: function(d) {
1112
+ var cr = c;
1113
+ for (var i = 0; i < d.length; ++i)
1114
+ cr = crct[cr & 255 ^ d[i]] ^ cr >>> 8;
1115
+ c = cr;
1116
+ },
1117
+ d: function() {
1118
+ return ~c;
1119
+ }
1120
+ };
1121
+ };
1122
+ var dopt = function(dat, opt, pre, post, st) {
1123
+ if (!st) {
1124
+ st = { l: 1 };
1125
+ if (opt.dictionary) {
1126
+ var dict = opt.dictionary.subarray(-32768);
1127
+ var newDat = new u8(dict.length + dat.length);
1128
+ newDat.set(dict);
1129
+ newDat.set(dat, dict.length);
1130
+ dat = newDat;
1131
+ st.w = dict.length;
1132
+ }
1133
+ }
1134
+ return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
1135
+ };
1136
+ var mrg = function(a, b) {
1137
+ var o = {};
1138
+ for (var k in a)
1139
+ o[k] = a[k];
1140
+ for (var k in b)
1141
+ o[k] = b[k];
1142
+ return o;
1143
+ };
1144
+ var wbytes = function(d, b, v) {
1145
+ for (; v; ++b)
1146
+ d[b] = v, v >>>= 8;
1147
+ };
1148
+ function deflateSync(data, opts) {
1149
+ return dopt(data, opts || {}, 0, 0);
1150
+ }
1151
+ var fltn = function(d, p, t, o) {
1152
+ for (var k in d) {
1153
+ var val = d[k], n = p + k, op = o;
1154
+ if (Array.isArray(val))
1155
+ op = mrg(o, val[1]), val = val[0];
1156
+ if (val instanceof u8)
1157
+ t[n] = [val, op];
1158
+ else {
1159
+ t[n += "/"] = [new u8(0), op];
1160
+ fltn(val, n, t, o);
1161
+ }
1162
+ }
1163
+ };
1164
+ var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
1165
+ var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
1166
+ var tds = 0;
1167
+ try {
1168
+ td.decode(et, { stream: true });
1169
+ tds = 1;
1170
+ } catch (e) {
1171
+ }
1172
+ function strToU8(str, latin1) {
1173
+ if (latin1) {
1174
+ var ar_1 = new u8(str.length);
1175
+ for (var i = 0; i < str.length; ++i)
1176
+ ar_1[i] = str.charCodeAt(i);
1177
+ return ar_1;
1178
+ }
1179
+ if (te)
1180
+ return te.encode(str);
1181
+ var l = str.length;
1182
+ var ar = new u8(str.length + (str.length >> 1));
1183
+ var ai = 0;
1184
+ var w = function(v) {
1185
+ ar[ai++] = v;
1186
+ };
1187
+ for (var i = 0; i < l; ++i) {
1188
+ if (ai + 5 > ar.length) {
1189
+ var n = new u8(ai + 8 + (l - i << 1));
1190
+ n.set(ar);
1191
+ ar = n;
1192
+ }
1193
+ var c = str.charCodeAt(i);
1194
+ if (c < 128 || latin1)
1195
+ w(c);
1196
+ else if (c < 2048)
1197
+ w(192 | c >> 6), w(128 | c & 63);
1198
+ else if (c > 55295 && c < 57344)
1199
+ c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
1200
+ else
1201
+ w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
1202
+ }
1203
+ return slc(ar, 0, ai);
1204
+ }
1205
+ var exfl = function(ex) {
1206
+ var le = 0;
1207
+ if (ex) {
1208
+ for (var k in ex) {
1209
+ var l = ex[k].length;
1210
+ if (l > 65535)
1211
+ err(9);
1212
+ le += l + 4;
1213
+ }
1214
+ }
1215
+ return le;
1216
+ };
1217
+ var wzh = function(d, b, f, fn, u, c, ce, co) {
1218
+ var fl2 = fn.length, ex = f.extra, col = co && co.length;
1219
+ var exl = exfl(ex);
1220
+ wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;
1221
+ if (ce != null)
1222
+ d[b++] = 20, d[b++] = f.os;
1223
+ d[b] = 20, b += 2;
1224
+ d[b++] = f.flag << 1 | (c < 0 && 8), d[b++] = u && 8;
1225
+ d[b++] = f.compression & 255, d[b++] = f.compression >> 8;
1226
+ var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;
1227
+ if (y < 0 || y > 119)
1228
+ err(10);
1229
+ wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;
1230
+ if (c != -1) {
1231
+ wbytes(d, b, f.crc);
1232
+ wbytes(d, b + 4, c < 0 ? -c - 2 : c);
1233
+ wbytes(d, b + 8, f.size);
1234
+ }
1235
+ wbytes(d, b + 12, fl2);
1236
+ wbytes(d, b + 14, exl), b += 16;
1237
+ if (ce != null) {
1238
+ wbytes(d, b, col);
1239
+ wbytes(d, b + 6, f.attrs);
1240
+ wbytes(d, b + 10, ce), b += 14;
1241
+ }
1242
+ d.set(fn, b);
1243
+ b += fl2;
1244
+ if (exl) {
1245
+ for (var k in ex) {
1246
+ var exf = ex[k], l = exf.length;
1247
+ wbytes(d, b, +k);
1248
+ wbytes(d, b + 2, l);
1249
+ d.set(exf, b + 4), b += 4 + l;
1250
+ }
1251
+ }
1252
+ if (col)
1253
+ d.set(co, b), b += col;
1254
+ return b;
1255
+ };
1256
+ var wzf = function(o, b, c, d, e) {
1257
+ wbytes(o, b, 101010256);
1258
+ wbytes(o, b + 8, c);
1259
+ wbytes(o, b + 10, c);
1260
+ wbytes(o, b + 12, d);
1261
+ wbytes(o, b + 16, e);
1262
+ };
1263
+ function zipSync(data, opts) {
1264
+ if (!opts)
1265
+ opts = {};
1266
+ var r = {};
1267
+ var files = [];
1268
+ fltn(data, "", r, opts);
1269
+ var o = 0;
1270
+ var tot = 0;
1271
+ for (var fn in r) {
1272
+ var _a2 = r[fn], file = _a2[0], p = _a2[1];
1273
+ var compression = p.level == 0 ? 0 : 8;
1274
+ var f = strToU8(fn), s = f.length;
1275
+ var com = p.comment, m = com && strToU8(com), ms = m && m.length;
1276
+ var exl = exfl(p.extra);
1277
+ if (s > 65535)
1278
+ err(11);
1279
+ var d = compression ? deflateSync(file, p) : file, l = d.length;
1280
+ var c = crc();
1281
+ c.p(file);
1282
+ files.push(mrg(p, {
1283
+ size: file.length,
1284
+ crc: c.d(),
1285
+ c: d,
1286
+ f,
1287
+ m,
1288
+ u: s != fn.length || m && com.length != ms,
1289
+ o,
1290
+ compression
1291
+ }));
1292
+ o += 30 + s + exl + l;
1293
+ tot += 76 + 2 * (s + exl) + (ms || 0) + l;
1294
+ }
1295
+ var out = new u8(tot + 22), oe = o, cdl = tot - o;
1296
+ for (var i = 0; i < files.length; ++i) {
1297
+ var f = files[i];
1298
+ wzh(out, f.o, f, f.f, f.u, f.c.length);
1299
+ var badd = 30 + f.f.length + exfl(f.extra);
1300
+ out.set(f.c, f.o + badd);
1301
+ wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);
1302
+ }
1303
+ wzf(out, o, files.length, cdl, oe);
1304
+ return out;
1305
+ }
1306
+
1307
+ // ../utils/src/zipper.ts
495
1308
  var Zipper = class {
496
1309
  outputPath;
1310
+ compressionLevel;
1311
+ onProgress;
497
1312
  constructor(options) {
498
1313
  this.outputPath = options.outputPath;
1314
+ this.compressionLevel = options.compressionLevel ?? 0;
1315
+ this.onProgress = options.onProgress;
499
1316
  }
500
1317
  /**
501
1318
  * Creates a ZIP archive containing the JSON report and assets
@@ -504,76 +1321,105 @@ var Zipper = class {
504
1321
  * @returns ZIP creation result with metadata
505
1322
  */
506
1323
  async createZip(reportPath, assets) {
1324
+ if (!fs4.existsSync(reportPath)) {
1325
+ throw new Error(`Report file not found: ${reportPath}`);
1326
+ }
1327
+ const transformedReportPath = this.transformJsonReport(reportPath);
1328
+ const files = {};
1329
+ const totalEntries = assets.length + 1;
1330
+ let processedEntries = 0;
1331
+ const reportContent = fs4.readFileSync(transformedReportPath);
1332
+ files["output/playwright-test-report.json"] = new Uint8Array(reportContent);
1333
+ processedEntries++;
1334
+ this.onProgress?.({ processedEntries, totalEntries, currentFile: "output/playwright-test-report.json" });
1335
+ for (const asset of assets) {
1336
+ if (!fs4.existsSync(asset.sourcePath)) {
1337
+ console.warn(`[Checkly Reporter] Skipping missing asset: ${asset.sourcePath}`);
1338
+ continue;
1339
+ }
1340
+ const content = fs4.readFileSync(asset.sourcePath);
1341
+ files[asset.archivePath] = new Uint8Array(content);
1342
+ processedEntries++;
1343
+ this.onProgress?.({ processedEntries, totalEntries, currentFile: asset.archivePath });
1344
+ }
1345
+ const zipData = zipSync(files, { level: this.compressionLevel });
1346
+ fs4.writeFileSync(this.outputPath, zipData);
1347
+ try {
1348
+ fs4.unlinkSync(transformedReportPath);
1349
+ } catch {
1350
+ }
1351
+ const entries = this.extractEntryOffsets(zipData);
1352
+ return {
1353
+ zipPath: this.outputPath,
1354
+ size: zipData.length,
1355
+ entryCount: entries.length,
1356
+ entries
1357
+ };
1358
+ }
1359
+ /**
1360
+ * Extracts byte offsets for each entry from the ZIP data
1361
+ * Parses the central directory to get accurate offset information
1362
+ */
1363
+ extractEntryOffsets(zipData) {
507
1364
  const entries = [];
508
- return new Promise((resolve2, reject) => {
509
- try {
510
- const output = fs3.createWriteStream(this.outputPath);
511
- const archive = new ZipArchive({
512
- zlib: { level: 0 }
513
- });
514
- archive.on("entry", (entryData) => {
515
- const entryName = entryData.name.replace(/\\/g, "/");
516
- const start = entryData._offsets?.contents ?? 0;
517
- const end = entryData._offsets?.contents + (entryData.csize ?? 0) - 1;
518
- entries.push({
519
- name: entryName,
520
- start,
521
- end
522
- });
523
- });
524
- output.on("close", () => {
525
- const zipSize = archive.pointer();
526
- resolve2({
527
- zipPath: this.outputPath,
528
- size: zipSize,
529
- entryCount: entries.length,
530
- entries
531
- });
532
- });
533
- archive.on("error", (err) => {
534
- reject(err);
535
- });
536
- output.on("error", (err) => {
537
- reject(err);
538
- });
539
- archive.pipe(output);
540
- if (!fs3.existsSync(reportPath)) {
541
- reject(new Error(`Report file not found: ${reportPath}`));
542
- return;
543
- }
544
- const transformedReportPath = this.transformJsonReport(reportPath);
545
- archive.file(transformedReportPath, { name: "output/playwright-test-report.json" });
546
- for (const asset of assets) {
547
- if (!fs3.existsSync(asset.sourcePath)) {
548
- console.warn(`[Checkly Reporter] Skipping missing asset: ${asset.sourcePath}`);
549
- continue;
550
- }
551
- archive.file(asset.sourcePath, { name: asset.archivePath });
1365
+ const view = new DataView(zipData.buffer, zipData.byteOffset, zipData.byteLength);
1366
+ let eocdOffset = -1;
1367
+ for (let i = zipData.length - 22; i >= 0; i--) {
1368
+ if (view.getUint32(i, true) === 101010256) {
1369
+ eocdOffset = i;
1370
+ break;
1371
+ }
1372
+ }
1373
+ if (eocdOffset === -1) {
1374
+ console.warn("[Checkly Reporter] Could not find ZIP end of central directory");
1375
+ return entries;
1376
+ }
1377
+ let cdOffset = view.getUint32(eocdOffset + 16, true);
1378
+ const cdSize = view.getUint32(eocdOffset + 12, true);
1379
+ if (cdOffset === 4294967295 && eocdOffset >= 20) {
1380
+ const zip64LocatorOffset = eocdOffset - 20;
1381
+ if (view.getUint32(zip64LocatorOffset, true) === 117853008) {
1382
+ const zip64EocdOffset = Number(view.getBigUint64(zip64LocatorOffset + 8, true));
1383
+ if (view.getUint32(zip64EocdOffset, true) === 101075792) {
1384
+ cdOffset = Number(view.getBigUint64(zip64EocdOffset + 48, true));
552
1385
  }
553
- archive.finalize();
554
- } catch (error) {
555
- reject(error);
556
1386
  }
557
- });
1387
+ }
1388
+ let offset = cdOffset;
1389
+ const cdEnd = cdOffset + cdSize;
1390
+ while (offset < cdEnd && view.getUint32(offset, true) === 33639248) {
1391
+ const compressedSize = view.getUint32(offset + 20, true);
1392
+ const filenameLength = view.getUint16(offset + 28, true);
1393
+ const extraLength = view.getUint16(offset + 30, true);
1394
+ const commentLength = view.getUint16(offset + 32, true);
1395
+ const localHeaderOffset = view.getUint32(offset + 42, true);
1396
+ const filenameBytes = zipData.slice(offset + 46, offset + 46 + filenameLength);
1397
+ const filename = new TextDecoder().decode(filenameBytes);
1398
+ const localExtraLength = view.getUint16(localHeaderOffset + 28, true);
1399
+ const dataStart = localHeaderOffset + 30 + filenameLength + localExtraLength;
1400
+ const dataEnd = dataStart + compressedSize - 1;
1401
+ entries.push({
1402
+ name: filename.replace(/\\/g, "/"),
1403
+ start: dataStart,
1404
+ end: dataEnd >= dataStart ? dataEnd : dataStart
1405
+ });
1406
+ offset += 46 + filenameLength + extraLength + commentLength;
1407
+ }
1408
+ return entries;
558
1409
  }
559
1410
  /**
560
1411
  * Transforms the JSON report to use relative paths for attachments
561
- * This ensures the UI can map attachment paths to ZIP entries
562
- * @param reportPath - Path to the original JSON report
563
- * @returns Path to the transformed JSON report (in temp directory)
564
1412
  */
565
1413
  transformJsonReport(reportPath) {
566
- const reportContent = fs3.readFileSync(reportPath, "utf-8");
1414
+ const reportContent = fs4.readFileSync(reportPath, "utf-8");
567
1415
  const report = JSON.parse(reportContent);
568
1416
  this.transformAttachmentPaths(report);
569
- const tempReportPath = path2.join(os.tmpdir(), `playwright-test-report-${Date.now()}.json`);
570
- fs3.writeFileSync(tempReportPath, JSON.stringify(report, null, 2));
1417
+ const tempReportPath = path3.join(os2.tmpdir(), `playwright-test-report-${Date.now()}.json`);
1418
+ fs4.writeFileSync(tempReportPath, JSON.stringify(report, null, 2));
571
1419
  return tempReportPath;
572
1420
  }
573
1421
  /**
574
1422
  * Recursively transforms attachment paths in the report structure
575
- * Converts absolute paths to relative paths matching ZIP structure
576
- * @param obj - Object to transform (mutated in place)
577
1423
  */
578
1424
  transformAttachmentPaths(obj) {
579
1425
  if (typeof obj !== "object" || obj === null) {
@@ -593,21 +1439,7 @@ var Zipper = class {
593
1439
  Object.values(obj).forEach((value) => this.transformAttachmentPaths(value));
594
1440
  }
595
1441
  /**
596
- * Normalizes attachment paths by extracting the relevant snapshot directory portion.
597
- * Supports Playwright's default and common custom snapshot directory patterns,
598
- * as well as blob merge resource paths.
599
- *
600
- * Priority order (first match wins):
601
- * 1. test-results/ (highest priority, existing behavior)
602
- * 2. blob-reports/resources/ (blob merge extraction paths)
603
- * 3. snapshots directories (Playwright default pattern)
604
- * 4. __screenshots__/ (common custom pattern)
605
- * 5. __snapshots__/ (common custom pattern)
606
- * 6. screenshots/ (simple custom pattern)
607
- * 7. snapshots/ (simple custom pattern)
608
- *
609
- * @param attachmentPath - Absolute or relative path to attachment
610
- * @returns Normalized path starting from the matched directory, or original path if no match
1442
+ * Normalizes attachment paths by extracting the relevant snapshot directory portion
611
1443
  */
612
1444
  normalizeAttachmentPath(attachmentPath) {
613
1445
  const normalizedPath = attachmentPath.replace(/\\/g, "/");
@@ -644,17 +1476,11 @@ var Zipper = class {
644
1476
  }
645
1477
  };
646
1478
 
647
- // src/reporter.ts
648
- import * as fs4 from "fs";
649
- import { readFileSync as readFileSync4 } from "fs";
650
- import * as path3 from "path";
651
- import { dirname, join as join3 } from "path";
652
- import { fileURLToPath } from "url";
653
-
654
- // ../clients/src/checkly-client.ts
655
- import axios from "axios";
1479
+ // src/api/client.ts
1480
+ var import_axios = __toESM(require("axios"));
1481
+ var import_form_data = __toESM(require("form-data"));
656
1482
 
657
- // ../clients/src/errors.ts
1483
+ // src/api/errors.ts
658
1484
  var ApiError = class extends Error {
659
1485
  data;
660
1486
  constructor(data, options) {
@@ -664,44 +1490,20 @@ var ApiError = class extends Error {
664
1490
  }
665
1491
  };
666
1492
  var ValidationError = class extends ApiError {
667
- constructor(data, options) {
668
- super(data, options);
669
- }
670
1493
  };
671
1494
  var UnauthorizedError = class extends ApiError {
672
- constructor(data, options) {
673
- super(data, options);
674
- }
675
1495
  };
676
1496
  var ForbiddenError = class extends ApiError {
677
- constructor(data, options) {
678
- super(data, options);
679
- }
680
1497
  };
681
1498
  var NotFoundError = class extends ApiError {
682
- constructor(data, options) {
683
- super(data, options);
684
- }
685
1499
  };
686
1500
  var RequestTimeoutError = class extends ApiError {
687
- constructor(data, options) {
688
- super(data, options);
689
- }
690
1501
  };
691
1502
  var ConflictError = class extends ApiError {
692
- constructor(data, options) {
693
- super(data, options);
694
- }
695
1503
  };
696
1504
  var ServerError = class extends ApiError {
697
- constructor(data, options) {
698
- super(data, options);
699
- }
700
1505
  };
701
1506
  var MiscellaneousError = class extends ApiError {
702
- constructor(data, options) {
703
- super(data, options);
704
- }
705
1507
  };
706
1508
  var MissingResponseError = class extends Error {
707
1509
  constructor(message, options) {
@@ -709,63 +1511,58 @@ var MissingResponseError = class extends Error {
709
1511
  this.name = "MissingResponseError";
710
1512
  }
711
1513
  };
712
- function parseErrorData(data, options) {
713
- if (!data) {
714
- return void 0;
715
- }
716
- if (typeof data === "object" && data.statusCode && data.error && data.message) {
717
- return {
718
- statusCode: data.statusCode,
719
- error: data.error,
720
- message: data.message,
721
- errorCode: data.errorCode
722
- };
723
- }
724
- if (typeof data === "object" && data.error && !data.message) {
725
- return {
726
- statusCode: options.statusCode,
727
- error: data.error,
728
- message: data.error
729
- };
730
- }
731
- if (typeof data === "object" && data.error && data.message) {
732
- return {
733
- statusCode: options.statusCode,
734
- error: data.error,
735
- message: data.message,
736
- errorCode: data.errorCode
737
- };
738
- }
739
- if (typeof data === "object" && data.message) {
740
- return {
741
- statusCode: options.statusCode,
742
- error: data.message,
743
- message: data.message,
744
- errorCode: data.errorCode
745
- };
1514
+ function parseErrorData(data, statusCode) {
1515
+ if (!data) return void 0;
1516
+ if (typeof data === "object" && data !== null) {
1517
+ const obj = data;
1518
+ if (obj.statusCode && obj.error && obj.message) {
1519
+ return {
1520
+ statusCode: obj.statusCode,
1521
+ error: obj.error,
1522
+ message: obj.message,
1523
+ errorCode: obj.errorCode
1524
+ };
1525
+ }
1526
+ if (obj.error && obj.message) {
1527
+ return {
1528
+ statusCode,
1529
+ error: obj.error,
1530
+ message: obj.message,
1531
+ errorCode: obj.errorCode
1532
+ };
1533
+ }
1534
+ if (obj.error) {
1535
+ return {
1536
+ statusCode,
1537
+ error: obj.error,
1538
+ message: obj.error
1539
+ };
1540
+ }
1541
+ if (obj.message) {
1542
+ return {
1543
+ statusCode,
1544
+ error: obj.message,
1545
+ message: obj.message,
1546
+ errorCode: obj.errorCode
1547
+ };
1548
+ }
746
1549
  }
747
1550
  if (typeof data === "string") {
748
- return {
749
- statusCode: options.statusCode,
750
- error: data,
751
- message: data
752
- };
1551
+ return { statusCode, error: data, message: data };
753
1552
  }
754
1553
  return void 0;
755
1554
  }
756
- function handleErrorResponse(err) {
757
- if (!err.response) {
758
- throw new MissingResponseError(err.message || "Network error");
759
- }
760
- const { status, data } = err.response;
761
- const errorData = parseErrorData(data, { statusCode: status });
762
- if (!errorData) {
763
- throw new MiscellaneousError({
764
- statusCode: status,
765
- error: "Unknown error",
766
- message: err.message || "An error occurred"
767
- });
768
- }
1555
+ function handleErrorResponse(err2) {
1556
+ const axiosError = err2;
1557
+ if (!axiosError.response) {
1558
+ throw new MissingResponseError(axiosError.message || "Network error");
1559
+ }
1560
+ const { status, data } = axiosError.response;
1561
+ const errorData = parseErrorData(data, status) ?? {
1562
+ statusCode: status,
1563
+ error: "Unknown error",
1564
+ message: axiosError.message || "An error occurred"
1565
+ };
769
1566
  switch (status) {
770
1567
  case 400:
771
1568
  throw new ValidationError(errorData);
@@ -787,715 +1584,905 @@ function handleErrorResponse(err) {
787
1584
  }
788
1585
  }
789
1586
 
790
- // ../clients/src/checkly-client.ts
791
- function getVersion() {
792
- return "0.1.0";
793
- }
794
- function createRequestInterceptor(apiKey, accountId) {
795
- return (config) => {
1587
+ // src/api/client.ts
1588
+ function createClient(options) {
1589
+ const http = import_axios.default.create({
1590
+ baseURL: options.baseUrl,
1591
+ timeout: 12e4,
1592
+ maxContentLength: Number.POSITIVE_INFINITY,
1593
+ maxBodyLength: Number.POSITIVE_INFINITY
1594
+ });
1595
+ http.interceptors.request.use((config) => {
796
1596
  if (config.headers) {
797
- config.headers.Authorization = `Bearer ${apiKey}`;
798
- config.headers["x-checkly-account"] = accountId;
799
- config.headers["User-Agent"] = `@checkly/playwright-reporter/${getVersion()}`;
1597
+ config.headers.Authorization = `Bearer ${options.apiKey}`;
1598
+ config.headers["x-checkly-account"] = options.accountId;
1599
+ config.headers["x-checkly-reporter-version"] = options.telemetry.reporterVersion;
1600
+ config.headers["x-checkly-playwright-version"] = options.telemetry.playwrightVersion;
1601
+ config.headers["x-checkly-ci-provider"] = options.telemetry.ciProvider;
1602
+ config.headers["x-checkly-ci-environment"] = options.telemetry.ciEnvironment;
1603
+ config.headers["x-checkly-node-version"] = options.telemetry.nodeVersion;
800
1604
  }
801
1605
  return config;
1606
+ });
1607
+ http.interceptors.response.use(
1608
+ (response) => response,
1609
+ (error) => handleErrorResponse(error)
1610
+ );
1611
+ return {
1612
+ testSessions: {
1613
+ async create(request) {
1614
+ const response = await http.post("/next/test-sessions/create", request);
1615
+ return response.data;
1616
+ },
1617
+ async uploadAsset(testSessionId, testResultId, assets) {
1618
+ const form = new import_form_data.default();
1619
+ form.append("assets", assets, { filename: "assets.zip", contentType: "application/zip" });
1620
+ const response = await http.post(
1621
+ `/next/test-sessions/${testSessionId}/results/${testResultId}/assets`,
1622
+ form,
1623
+ { headers: form.getHeaders() }
1624
+ );
1625
+ return response.data;
1626
+ },
1627
+ async updateResult(testSessionId, testResultId, request) {
1628
+ const response = await http.post(
1629
+ `/next/test-sessions/${testSessionId}/results/${testResultId}`,
1630
+ request
1631
+ );
1632
+ return response.data;
1633
+ }
1634
+ }
802
1635
  };
803
1636
  }
804
- function createResponseErrorInterceptor() {
805
- return (error) => {
806
- handleErrorResponse(error);
807
- };
808
- }
809
- var ChecklyClient = class {
810
- apiKey;
811
- baseUrl;
812
- accountId;
813
- api;
814
- constructor(options) {
815
- this.accountId = options.accountId;
816
- this.apiKey = options.apiKey;
817
- this.baseUrl = options.baseUrl;
818
- this.api = axios.create({
819
- baseURL: this.baseUrl,
820
- timeout: 12e4,
821
- // 120 second timeout for large uploads
822
- maxContentLength: Number.POSITIVE_INFINITY,
823
- // Allow large payloads
824
- maxBodyLength: Number.POSITIVE_INFINITY
825
- // Allow large request bodies
826
- });
827
- this.api.interceptors.request.use(createRequestInterceptor(this.apiKey, this.accountId));
828
- this.api.interceptors.response.use((response) => response, createResponseErrorInterceptor());
829
- }
830
- /**
831
- * Gets the underlying axios instance
832
- * Useful for creating resource-specific clients (e.g., TestResults)
833
- */
834
- getAxiosInstance() {
835
- return this.api;
836
- }
837
- };
838
1637
 
839
- // ../clients/src/test-results.ts
840
- import FormData from "form-data";
841
- var TestResults = class {
842
- constructor(api) {
843
- this.api = api;
844
- }
845
- /**
846
- * Creates a new test session in Checkly
847
- *
848
- * @param request Test session creation request
849
- * @returns Test session response with session ID and test result IDs
850
- * @throws {ValidationError} If request data is invalid
851
- * @throws {UnauthorizedError} If authentication fails
852
- * @throws {ServerError} If server error occurs
853
- */
854
- async createTestSession(request) {
855
- const response = await this.api.post("/next/test-sessions/create", request);
856
- return response.data;
857
- }
858
- /**
859
- * Step 1: Upload test result assets to S3
860
- * Streams a ZIP file containing test assets (traces, videos, screenshots)
861
- *
862
- * @param testSessionId ID of the test session
863
- * @param testResultId ID of the test result
864
- * @param assets Buffer or ReadableStream of the ZIP file
865
- * @returns Upload response with assetId, region, key, and url
866
- * @throws {ValidationError} If assets are invalid
867
- * @throws {UnauthorizedError} If authentication fails
868
- * @throws {NotFoundError} If test session or result not found
869
- * @throws {PayloadTooLargeError} If assets exceed 500MB
870
- * @throws {ServerError} If S3 upload fails
871
- */
872
- async uploadTestResultAsset(testSessionId, testResultId, assets) {
873
- const form = new FormData();
874
- form.append("assets", assets, {
875
- filename: "assets.zip",
876
- contentType: "application/zip"
877
- });
878
- const response = await this.api.post(
879
- `/next/test-sessions/${testSessionId}/results/${testResultId}/assets`,
880
- form,
881
- {
882
- headers: {
883
- ...form.getHeaders()
884
- }
885
- }
886
- );
887
- return response.data;
888
- }
889
- /**
890
- * Step 2: Update test result with status and optional asset reference
891
- * Uses JSON payload for clean, easy-to-validate updates
892
- *
893
- * @param testSessionId ID of the test session
894
- * @param testResultId ID of the test result to update
895
- * @param request Test result update request (JSON)
896
- * @returns Test result update response
897
- * @throws {ValidationError} If request data is invalid
898
- * @throws {UnauthorizedError} If authentication fails
899
- * @throws {NotFoundError} If test session or result not found
900
- * @throws {ServerError} If server error occurs
901
- */
902
- async updateTestResult(testSessionId, testResultId, request) {
903
- const response = await this.api.post(
904
- `/next/test-sessions/${testSessionId}/results/${testResultId}`,
905
- request
1638
+ // src/extensions/checkly-upload.ts
1639
+ var MissingCredentialsError = class extends Error {
1640
+ constructor(missing) {
1641
+ const missingList = missing.map((m) => m === "CHECKLY_API_KEY" ? "apiKey / CHECKLY_API_KEY" : "accountId / CHECKLY_ACCOUNT_ID").join(", ");
1642
+ super(
1643
+ `Missing required Checkly credentials: ${missingList}.
1644
+ Provide them via reporter options or environment variables.
1645
+ Get your API key at: https://app.checklyhq.com/settings/user/api-keys
1646
+ Or use dryRun: true for local report generation.`
906
1647
  );
907
- return response.data;
1648
+ this.name = "MissingCredentialsError";
908
1649
  }
909
1650
  };
910
-
911
- // src/reporter.ts
912
- var __filename = fileURLToPath(import.meta.url);
913
- var __dirname = dirname(__filename);
914
- var packageJson = JSON.parse(readFileSync4(join3(__dirname, "..", "package.json"), "utf-8"));
915
- var pkgVersion = packageJson.version;
916
- var pluralRules = new Intl.PluralRules("en-US");
917
- var projectForms = {
918
- zero: "Project",
919
- one: "Project",
920
- two: "Projects",
921
- few: "Projects",
922
- many: "Projects",
923
- other: "Projects"
924
- };
1651
+ function getPackageVersion() {
1652
+ try {
1653
+ const packageJson = JSON.parse(fs5.readFileSync(path4.join(__dirname, "..", "package.json"), "utf-8"));
1654
+ return packageJson.version;
1655
+ } catch {
1656
+ return "unknown";
1657
+ }
1658
+ }
925
1659
  function getApiUrl(environment) {
926
- const environments = {
1660
+ const urls = {
927
1661
  local: "http://127.0.0.1:3000",
928
1662
  development: "https://api-dev.checklyhq.com",
929
1663
  staging: "https://api-test.checklyhq.com",
930
1664
  production: "https://api.checklyhq.com"
931
1665
  };
932
- return environments[environment];
1666
+ return urls[environment];
933
1667
  }
934
- function getEnvironment(options) {
935
- const envFromOptions = options?.environment;
936
- const envFromEnvVar = process.env.CHECKLY_ENV;
937
- const env = envFromOptions || envFromEnvVar || "production";
938
- const validEnvironments = ["local", "development", "staging", "production"];
939
- if (!validEnvironments.includes(env)) {
940
- console.warn(`[Checkly Reporter] Invalid environment "${env}", using "production"`);
1668
+ function getEnvironment() {
1669
+ const env = process.env.CHECKLY_ENV || "production";
1670
+ const valid = ["local", "development", "staging", "production"];
1671
+ if (!valid.includes(env)) {
1672
+ console.warn(`[Checkly] Invalid environment "${env}", using "production"`);
941
1673
  return "production";
942
1674
  }
943
1675
  return env;
944
1676
  }
945
- function convertStepToJSON(step) {
946
- return {
947
- title: step.title,
948
- duration: step.duration,
949
- error: step.error,
950
- steps: step.steps.length > 0 ? step.steps.map(convertStepToJSON) : void 0
951
- };
952
- }
953
1677
  function getDirectoryName() {
954
1678
  const cwd = process.cwd();
955
- let dirName = path3.basename(cwd);
956
- if (!dirName || dirName === "/" || dirName === ".") {
957
- dirName = "playwright-tests";
958
- }
959
- dirName = dirName.replace(/[<>:"|?*]/g, "-");
960
- if (dirName.length > 255) {
961
- dirName = dirName.substring(0, 255);
962
- }
963
- return dirName;
1679
+ let name = path4.basename(cwd);
1680
+ if (!name || name === "/" || name === ".") name = "playwright-tests";
1681
+ name = name.replace(/[<>:"|?*]/g, "-");
1682
+ if (name.length > 255) name = name.substring(0, 255);
1683
+ return name;
964
1684
  }
965
- var ChecklyReporter = class {
966
- options;
967
- assetCollector;
968
- zipper;
969
- testResults;
970
- testSession;
971
- startTime;
972
- testCounts = {
973
- passed: 0,
974
- failed: 0,
975
- flaky: 0
976
- };
977
- // Store steps per test result, keyed by "testId:retry"
978
- stepsMap = /* @__PURE__ */ new Map();
979
- // Store warnings per test result, keyed by "testId:retry"
980
- warningsMap = /* @__PURE__ */ new Map();
981
- // Store trace file paths per test result, keyed by "testId:retry"
982
- tracePathsMap = /* @__PURE__ */ new Map();
983
- // Store console messages per test result, keyed by "testId:retry"
984
- consoleMessagesMap = /* @__PURE__ */ new Map();
985
- // Store network requests per test result, keyed by "testId:retry"
986
- networkRequestsMap = /* @__PURE__ */ new Map();
987
- /**
988
- * Log a message if verbose mode is enabled
989
- */
990
- log(message, data) {
991
- if (!this.options.verbose) return;
992
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
993
- if (data) {
994
- console.log(`[Checkly Reporter DEBUG ${timestamp}] ${message}`, JSON.stringify(data, null, 2));
995
- } else {
996
- console.log(`[Checkly Reporter DEBUG ${timestamp}] ${message}`);
997
- }
998
- }
999
- constructor(options = {}) {
1000
- const environment = getEnvironment(options);
1001
- const baseUrl = getApiUrl(environment);
1002
- const apiKey = process.env.CHECKLY_API_KEY || options.apiKey;
1003
- const accountId = process.env.CHECKLY_ACCOUNT_ID || options.accountId;
1004
- const verbose = options.verbose ?? process.env.CHECKLY_REPORTER_VERBOSE === "true";
1005
- this.options = {
1006
- accountId,
1007
- apiKey,
1008
- outputPath: options.outputPath ?? "checkly-report.zip",
1009
- jsonReportPath: options.jsonReportPath ?? "test-results/playwright-test-report.json",
1010
- testResultsDir: options.testResultsDir ?? "test-results",
1011
- dryRun: options.dryRun ?? false,
1012
- sessionName: options.sessionName,
1013
- verbose
1014
- };
1015
- this.assetCollector = new AssetCollector(this.options.testResultsDir);
1016
- this.zipper = new Zipper({
1017
- outputPath: this.options.outputPath
1018
- });
1019
- if (!this.options.dryRun && this.options.apiKey && this.options.accountId) {
1020
- const client = new ChecklyClient({
1021
- apiKey: this.options.apiKey,
1022
- accountId: this.options.accountId,
1023
- baseUrl
1024
- });
1025
- this.testResults = new TestResults(client.getAxiosInstance());
1026
- }
1027
- if (verbose) {
1028
- console.log(`[Checkly Reporter DEBUG] Initialized with options:`, {
1029
- environment,
1030
- baseUrl,
1031
- hasApiKey: !!apiKey,
1032
- hasAccountId: !!accountId,
1033
- outputPath: this.options.outputPath,
1034
- jsonReportPath: this.options.jsonReportPath,
1035
- testResultsDir: this.options.testResultsDir,
1036
- dryRun: this.options.dryRun,
1037
- verbose: this.options.verbose,
1038
- hasTestResults: !!this.testResults
1685
+ function checklyUpload(options = {}) {
1686
+ const apiKey = process.env.CHECKLY_API_KEY || options.apiKey;
1687
+ const accountId = process.env.CHECKLY_ACCOUNT_ID || options.accountId;
1688
+ const environment = getEnvironment();
1689
+ const baseUrl = getApiUrl(environment);
1690
+ const dryRun = options.dryRun ?? false;
1691
+ const ciInfo = detectCI();
1692
+ const reporterVersion = getPackageVersion();
1693
+ const canUpload = !dryRun && apiKey && accountId;
1694
+ let api;
1695
+ let testSession;
1696
+ let sessionCreationPromise;
1697
+ let startTime;
1698
+ let resolvedOutputDir;
1699
+ let assetCollector;
1700
+ let zipper;
1701
+ const testCounts = { passed: 0, failed: 0, flaky: 0 };
1702
+ const tracePathsMap = /* @__PURE__ */ new Map();
1703
+ const warningsMap = /* @__PURE__ */ new Map();
1704
+ const consoleMessagesMap = /* @__PURE__ */ new Map();
1705
+ const networkRequestsMap = /* @__PURE__ */ new Map();
1706
+ function resolveSessionName(ctx) {
1707
+ const { sessionName } = options;
1708
+ if (typeof sessionName === "function") return sessionName(ctx);
1709
+ if (typeof sessionName === "string") return sessionName;
1710
+ return `Playwright Test Session: ${ctx.directoryName}`;
1711
+ }
1712
+ async function createSession(config, suite, log) {
1713
+ if (!api) return;
1714
+ try {
1715
+ const directoryName = getDirectoryName();
1716
+ const sessionName = resolveSessionName({ directoryName, config, suite });
1717
+ const repoInfo = getGitHubRepoInfo();
1718
+ log("\u{1F517} Creating test session", { name: sessionName, environment });
1719
+ testSession = await api.testSessions.create({
1720
+ name: sessionName,
1721
+ environment: process.env.NODE_ENV || "test",
1722
+ repoInfo,
1723
+ startedAt: startTime.getTime(),
1724
+ testResults: [{ name: directoryName }],
1725
+ provider: "PW_REPORTER"
1039
1726
  });
1727
+ log("\u2705 Session created", { id: testSession.testSessionId });
1728
+ } catch (err2) {
1729
+ if (err2 instanceof UnauthorizedError) {
1730
+ console.error(
1731
+ "[Checkly] Authentication failed: Invalid API key.\n Please verify your apiKey option or CHECKLY_API_KEY environment variable is correct.\n Get your API key at: https://app.checklyhq.com/settings/user/api-keys"
1732
+ );
1733
+ api = void 0;
1734
+ return;
1735
+ }
1736
+ if (err2 instanceof ForbiddenError) {
1737
+ console.error(
1738
+ "[Checkly] Access denied: You do not have access to this account.\n Please verify your accountId option or CHECKLY_ACCOUNT_ID environment variable is correct.\n Get your API key at: https://app.checklyhq.com/settings/user/api-keys"
1739
+ );
1740
+ api = void 0;
1741
+ return;
1742
+ }
1743
+ console.error("[Checkly] Failed to create test session:", err2);
1040
1744
  }
1041
1745
  }
1042
- /**
1043
- * Resolves the session name from options
1044
- * Supports string, callback function, or falls back to default
1045
- */
1046
- resolveSessionName(context) {
1047
- const { sessionName } = this.options;
1048
- if (typeof sessionName === "function") {
1049
- return sessionName(context);
1050
- }
1051
- if (typeof sessionName === "string") {
1052
- return sessionName;
1053
- }
1054
- return `Playwright Test Session: ${context.directoryName}`;
1055
- }
1056
- /**
1057
- * Checks if test result has a trace attachment and adds context-aware warning if missing
1058
- * Also captures trace file path for later console message extraction
1059
- * The warning type depends on the trace configuration and test result state
1060
- */
1061
- checkTraceAttachment(test, result) {
1746
+ function checkTraceAttachment(test, result) {
1062
1747
  const key = `${test.id}:${result.retry}`;
1063
- const traceAttachment = result.attachments?.find(
1064
- (attachment) => attachment.name === "trace" || attachment.contentType === "application/zip"
1065
- );
1748
+ const traceAttachment = result.attachments?.find((a) => a.name === "trace" || a.contentType === "application/zip");
1066
1749
  if (traceAttachment?.path) {
1067
- this.tracePathsMap.set(key, traceAttachment.path);
1750
+ tracePathsMap.set(key, traceAttachment.path);
1068
1751
  return;
1069
1752
  }
1070
1753
  const traceConfig = test.parent?.project()?.use?.trace;
1071
1754
  const traceMode = typeof traceConfig === "object" ? traceConfig.mode : traceConfig;
1072
- const isRetry = result.retry > 0;
1073
- const testPassed = result.status === "passed";
1074
- let warningType;
1075
- let message;
1076
- switch (traceMode) {
1077
- case void 0:
1078
- return;
1079
- case "off":
1080
- warningType = "trace-off";
1081
- message = 'Traces are disabled. Set trace: "on" in playwright.config.ts to capture traces.';
1082
- break;
1083
- case "retain-on-failure":
1084
- if (testPassed) {
1085
- warningType = "trace-retained-on-failure";
1086
- message = 'No trace retained because test passed. Trace mode is "retain-on-failure" which discards traces for passing tests.';
1087
- } else {
1088
- warningType = "trace-missing";
1089
- message = 'Trace should exist but was not found. The test failed with trace: "retain-on-failure".';
1090
- }
1091
- break;
1092
- case "on-first-retry":
1093
- if (!isRetry) {
1094
- warningType = "trace-first-retry-only";
1095
- message = 'No trace for initial attempt. Trace mode is "on-first-retry" which only records traces on the first retry.';
1096
- } else if (result.retry === 1) {
1097
- warningType = "trace-missing";
1098
- message = 'Trace should exist but was not found. This is the first retry with trace: "on-first-retry".';
1099
- } else {
1100
- warningType = "trace-first-retry-only";
1101
- message = `No trace for retry #${result.retry}. Trace mode is "on-first-retry" which only records the first retry.`;
1102
- }
1103
- break;
1104
- case "on-all-retries":
1105
- if (!isRetry) {
1106
- warningType = "trace-retries-only";
1107
- message = 'No trace for initial attempt. Trace mode is "on-all-retries" which only records traces on retries.';
1108
- } else {
1109
- warningType = "trace-missing";
1110
- message = `Trace should exist but was not found. This is retry #${result.retry} with trace: "on-all-retries".`;
1111
- }
1112
- break;
1113
- case "retain-on-first-failure":
1114
- if (testPassed) {
1115
- warningType = "trace-retained-on-first-failure";
1116
- message = 'No trace retained because test passed. Trace mode is "retain-on-first-failure" which discards traces for passing tests.';
1117
- } else if (isRetry) {
1118
- warningType = "trace-retained-on-first-failure";
1119
- message = 'No trace for retries. Trace mode is "retain-on-first-failure" which only records the first run.';
1120
- } else {
1121
- warningType = "trace-missing";
1122
- message = 'Trace should exist but was not found. The test failed on first run with trace: "retain-on-first-failure".';
1755
+ if (!traceMode || traceMode === "off") return;
1756
+ const warnings = warningsMap.get(key) || [];
1757
+ warnings.push({ type: "trace-missing", message: `No trace found for trace mode "${traceMode}"` });
1758
+ warningsMap.set(key, warnings);
1759
+ }
1760
+ async function extractTraceData(key, tracePath) {
1761
+ const reader = new TraceReader(tracePath);
1762
+ if (!await reader.open()) return;
1763
+ const messages = await reader.extractEvents('"type":"console"', toConsoleMessage);
1764
+ if (messages.length > 0) consoleMessagesMap.set(key, messages);
1765
+ const requests = await reader.extractEvents('"type":"resource-snapshot"', toNetworkRequest);
1766
+ if (requests.length > 0) networkRequestsMap.set(key, requests);
1767
+ }
1768
+ async function extractDataFromTraces(log) {
1769
+ if (tracePathsMap.size === 0) return;
1770
+ log("\u{1F4E6} Extracting trace data", { traces: tracePathsMap.size });
1771
+ const extractions = Array.from(tracePathsMap.entries()).map(
1772
+ ([key, tracePath]) => extractTraceData(key, tracePath).catch((err2) => {
1773
+ console.error(`[Checkly] Failed to extract trace data: ${err2}`);
1774
+ })
1775
+ );
1776
+ await Promise.all(extractions);
1777
+ }
1778
+ function buildChecklyExtensionData(key) {
1779
+ const warnings = warningsMap.get(key);
1780
+ const consoleMessages = consoleMessagesMap.get(key);
1781
+ const network = networkRequestsMap.get(key);
1782
+ if (!warnings?.length && !consoleMessages?.length && !network?.length) {
1783
+ return null;
1784
+ }
1785
+ return {
1786
+ ...warnings?.length ? { warnings } : {},
1787
+ ...consoleMessages?.length ? { console: consoleMessages } : {},
1788
+ ...network?.length ? { network } : {}
1789
+ };
1790
+ }
1791
+ function injectDataIntoReport(report) {
1792
+ function processSuite(suite) {
1793
+ for (const spec of suite.specs) {
1794
+ for (const test of spec.tests) {
1795
+ for (const result of test.results) {
1796
+ const key = `${spec.id}:${result.retry}`;
1797
+ const checklyData = buildChecklyExtensionData(key);
1798
+ if (checklyData) {
1799
+ ;
1800
+ result._checkly = checklyData;
1801
+ }
1802
+ }
1123
1803
  }
1124
- break;
1125
- case "on":
1126
- warningType = "trace-missing";
1127
- message = 'Trace should exist but was not found. Trace mode is "on" which should always record traces.';
1128
- break;
1129
- default:
1130
- warningType = "trace-missing";
1131
- message = `No trace found. Trace mode "${traceMode}" may not be generating traces for this result.`;
1804
+ }
1805
+ suite.suites?.forEach(processSuite);
1132
1806
  }
1133
- const warnings = this.warningsMap.get(key) || [];
1134
- warnings.push({ type: warningType, message });
1135
- this.warningsMap.set(key, warnings);
1807
+ report.suites.forEach(processSuite);
1136
1808
  }
1137
- /**
1138
- * Called once before running tests
1139
- * Creates test session in Checkly if credentials provided
1140
- */
1141
- onBegin(config, suite) {
1142
- this.startTime = /* @__PURE__ */ new Date();
1143
- if (!this.testResults) {
1144
- return;
1809
+ function calculateTiming(report) {
1810
+ const stats = report.stats;
1811
+ if (stats?.startTime && stats?.duration !== void 0) {
1812
+ const start = new Date(stats.startTime);
1813
+ const responseTime = Math.round(stats.duration);
1814
+ return {
1815
+ startedAt: start.toISOString(),
1816
+ stoppedAt: new Date(start.getTime() + responseTime).toISOString(),
1817
+ responseTime
1818
+ };
1819
+ }
1820
+ const now = Date.now();
1821
+ return {
1822
+ startedAt: startTime?.toISOString() ?? (/* @__PURE__ */ new Date()).toISOString(),
1823
+ stoppedAt: new Date(now).toISOString(),
1824
+ responseTime: startTime ? Math.round(now - startTime.getTime()) : 0
1825
+ };
1826
+ }
1827
+ async function uploadAssets(log, zipPath, zipSizeBytes) {
1828
+ if (!api || !testSession || zipSizeBytes === 0) return void 0;
1829
+ const firstResult = testSession.testResults[0];
1830
+ if (!firstResult) return void 0;
1831
+ log("\u{1F4E4} Uploading assets", { size: `${(zipSizeBytes / 1024).toFixed(1)}KB` });
1832
+ try {
1833
+ const assets = fs5.createReadStream(zipPath);
1834
+ const uploadResponse = await api.testSessions.uploadAsset(
1835
+ testSession.testSessionId,
1836
+ firstResult.testResultId,
1837
+ assets
1838
+ );
1839
+ log("\u2705 Assets uploaded", { assetId: uploadResponse.assetId });
1840
+ return uploadResponse.assetId;
1841
+ } catch (err2) {
1842
+ console.error("[Checkly] Asset upload failed:", err2);
1843
+ return void 0;
1145
1844
  }
1845
+ }
1846
+ async function uploadResults(log, report, zipPath, entries) {
1847
+ if (!api || !testSession || testSession.testResults.length === 0) return;
1146
1848
  try {
1147
- const directoryName = getDirectoryName();
1148
- const sessionName = this.resolveSessionName({ directoryName, config, suite });
1149
- const testResults = [{ name: directoryName }];
1150
- const repoUrl = process.env.GITHUB_REPOSITORY ? `https://github.com/${process.env.GITHUB_REPOSITORY}` : void 0;
1151
- const repoInfo = repoUrl ? {
1152
- repoUrl,
1153
- commitId: process.env.GITHUB_SHA,
1154
- branchName: process.env.GITHUB_REF_NAME,
1155
- commitOwner: process.env.GITHUB_ACTOR,
1156
- commitMessage: process.env.GITHUB_EVENT_NAME
1157
- } : void 0;
1158
- this.testResults.createTestSession({
1159
- name: sessionName,
1160
- environment: process.env.NODE_ENV || "test",
1161
- repoInfo,
1162
- startedAt: this.startTime.getTime(),
1163
- // Required timestamp in milliseconds
1164
- testResults,
1165
- provider: "PW_REPORTER"
1166
- }).then((response) => {
1167
- this.testSession = response;
1168
- }).catch((error) => {
1169
- console.error("[Checkly Reporter] Failed to create test session:", error.message);
1849
+ const { failed, flaky } = testCounts;
1850
+ const overallStatus = failed > 0 ? "FAILED" : "PASSED";
1851
+ const isDegraded = failed === 0 && flaky > 0;
1852
+ const timing = calculateTiming(report);
1853
+ const zipSizeBytes = (await fs5.promises.stat(zipPath)).size;
1854
+ const assetId = await uploadAssets(log, zipPath, zipSizeBytes);
1855
+ const firstResult = testSession.testResults[0];
1856
+ log("\u{1F4DD} Updating test result", { status: overallStatus, isDegraded });
1857
+ await api.testSessions.updateResult(testSession.testSessionId, firstResult.testResultId, {
1858
+ status: overallStatus,
1859
+ assetEntries: assetId ? entries : void 0,
1860
+ isDegraded,
1861
+ ...timing,
1862
+ metadata: { usageData: { s3PostTotalBytes: zipSizeBytes } }
1170
1863
  });
1171
- } catch (error) {
1172
- console.error("[Checkly Reporter] Error in onBegin:", error);
1864
+ } catch (err2) {
1865
+ console.error("[Checkly] Failed to upload results:", err2);
1173
1866
  }
1174
1867
  }
1175
- /**
1176
- * Called for each test when it completes
1177
- * Captures steps and warnings, tracks test results for final status calculation
1178
- */
1179
- onTestEnd(test, result) {
1180
- try {
1181
- this.checkTraceAttachment(test, result);
1182
- const stepsKey = `${test.id}:${result.retry}`;
1183
- if (result.steps && result.steps.length > 0) {
1184
- this.stepsMap.set(stepsKey, result.steps.map(convertStepToJSON));
1868
+ return {
1869
+ name: "checkly-upload",
1870
+ onBegin: ({ config, suite, log }) => {
1871
+ startTime = /* @__PURE__ */ new Date();
1872
+ resolvedOutputDir = options.outputDir ?? config.projects[0]?.outputDir ?? "test-results";
1873
+ const zipPath = path4.join(resolvedOutputDir, "checkly-report.zip");
1874
+ assetCollector = new AssetCollector(resolvedOutputDir);
1875
+ zipper = new Zipper({ outputPath: zipPath });
1876
+ if (!canUpload) {
1877
+ if (dryRun) {
1878
+ console.warn("[Checkly] Dry run mode - report will be generated locally");
1879
+ return;
1880
+ }
1881
+ const missing = [];
1882
+ if (!apiKey) missing.push("CHECKLY_API_KEY");
1883
+ if (!accountId) missing.push("CHECKLY_ACCOUNT_ID");
1884
+ throw new MissingCredentialsError(missing);
1185
1885
  }
1886
+ api = createClient({
1887
+ apiKey,
1888
+ accountId,
1889
+ baseUrl,
1890
+ telemetry: {
1891
+ reporterVersion,
1892
+ playwrightVersion: config.version,
1893
+ ciProvider: ciInfo.ciProvider,
1894
+ ciEnvironment: ciInfo.environment,
1895
+ nodeVersion: process.version
1896
+ }
1897
+ });
1898
+ sessionCreationPromise = createSession(config, suite, log);
1899
+ },
1900
+ onTestEnd: ({ test, result }) => {
1901
+ checkTraceAttachment(test, result);
1186
1902
  const outcome = test.outcome();
1187
1903
  const testIsComplete = result.retry === test.retries || outcome !== "unexpected";
1188
- if (!testIsComplete) {
1189
- return;
1904
+ if (!testIsComplete) return;
1905
+ if (outcome === "flaky") {
1906
+ testCounts.flaky++;
1907
+ testCounts.passed++;
1908
+ } else if (result.status === "passed") {
1909
+ testCounts.passed++;
1910
+ } else if (result.status === "failed" || result.status === "timedOut") {
1911
+ testCounts.failed++;
1190
1912
  }
1191
- const isFlaky = outcome === "flaky";
1192
- if (isFlaky) {
1193
- this.testCounts.flaky++;
1194
- this.testCounts.passed++;
1195
- } else {
1196
- if (result.status === "passed") {
1197
- this.testCounts.passed++;
1198
- } else if (result.status === "failed" || result.status === "timedOut") {
1199
- this.testCounts.failed++;
1913
+ },
1914
+ onEnd: async ({ report, log, addSummaryLine }) => {
1915
+ try {
1916
+ if (sessionCreationPromise) {
1917
+ await sessionCreationPromise;
1200
1918
  }
1201
- }
1202
- } catch (error) {
1203
- console.error("[Checkly Reporter] Error in onTestEnd:", error);
1204
- }
1205
- }
1206
- /**
1207
- * Called after all tests have completed
1208
- * This is where we create the ZIP archive and upload results
1209
- */
1210
- async onEnd() {
1211
- this.log("onEnd started", {
1212
- testCounts: this.testCounts,
1213
- stepsMapSize: this.stepsMap.size,
1214
- warningsMapSize: this.warningsMap.size,
1215
- hasTestSession: !!this.testSession,
1216
- testSessionId: this.testSession?.testSessionId
1217
- });
1218
- try {
1219
- const jsonReportPath = this.options.jsonReportPath;
1220
- if (!fs4.existsSync(jsonReportPath)) {
1221
- console.error(`[Checkly Reporter] ERROR: JSON report not found at: ${jsonReportPath}`);
1222
- console.error("[Checkly Reporter] Make sure to configure the json reporter before the checkly reporter:");
1223
- console.error(
1224
- " reporter: [\n ['json', { outputFile: 'test-results/playwright-test-report.json' }],\n ['@checkly/playwright-reporter']\n ]"
1225
- );
1226
- return;
1227
- }
1228
- this.log("Reading JSON report", { path: jsonReportPath });
1229
- const reportContent = fs4.readFileSync(jsonReportPath, "utf-8");
1230
- const report = JSON.parse(reportContent);
1231
- this.log("JSON report parsed", {
1232
- configVersion: report.config?.version,
1233
- projectsCount: report.config?.projects?.length ?? 0,
1234
- suitesCount: report.suites?.length ?? 0,
1235
- rootDir: report.config?.rootDir
1236
- });
1237
- await this.extractDataFromTraces();
1238
- this.injectDataIntoReport(report);
1239
- this.log("Data injected into report", {
1240
- projectsCountAfterReconstruction: report.config?.projects?.length ?? 0
1241
- });
1242
- fs4.writeFileSync(jsonReportPath, JSON.stringify(report, null, 2), "utf-8");
1243
- this.log("Enriched report written to disk");
1244
- this.log("Collecting assets", { testResultsDir: this.options.testResultsDir });
1245
- const assets = await this.assetCollector.collectAssets(report);
1246
- this.log("Assets collected", {
1247
- count: assets.length,
1248
- assets: assets.map((a) => ({ source: a.sourcePath, archive: a.archivePath, type: a.type }))
1249
- });
1250
- this.log("Creating ZIP archive", { outputPath: this.options.outputPath });
1251
- const result = await this.zipper.createZip(jsonReportPath, assets);
1252
- this.log("ZIP created", {
1253
- zipPath: result.zipPath,
1254
- zipSize: result.size,
1255
- entriesCount: result.entries.length,
1256
- entries: result.entries.map((e) => ({ name: e.name, start: e.start, end: e.end }))
1257
- });
1258
- if (this.testResults && this.testSession) {
1259
- this.log("Uploading results", { testSessionId: this.testSession.testSessionId });
1260
- await this.uploadResults(report, result.zipPath, result.entries);
1261
- if (!this.options.dryRun) {
1262
- try {
1263
- fs4.unlinkSync(result.zipPath);
1264
- } catch (cleanupError) {
1265
- console.warn(`[Checkly Reporter] Warning: Could not delete ZIP file: ${cleanupError}`);
1919
+ await extractDataFromTraces(log);
1920
+ injectDataIntoReport(report);
1921
+ fs5.mkdirSync(resolvedOutputDir, { recursive: true });
1922
+ const tempReportPath = path4.join(resolvedOutputDir, `.checkly-report-${Date.now()}.json`);
1923
+ fs5.writeFileSync(tempReportPath, JSON.stringify(report, null, 2));
1924
+ const assets = await assetCollector.collectAssets(report);
1925
+ const zipResult = await zipper.createZip(tempReportPath, assets);
1926
+ log("\u{1F4E6} ZIP created", { path: zipResult.zipPath, size: `${(zipResult.size / 1024).toFixed(1)}KB` });
1927
+ if (api && testSession) {
1928
+ await uploadResults(log, report, zipResult.zipPath, zipResult.entries);
1929
+ if (!dryRun) {
1930
+ try {
1931
+ fs5.unlinkSync(zipResult.zipPath);
1932
+ } catch {
1933
+ }
1934
+ }
1935
+ if (testSession.link) {
1936
+ addSummaryLine(`\u{1F517} Checkly Test Session: ${testSession.link}`);
1266
1937
  }
1267
1938
  }
1939
+ try {
1940
+ fs5.unlinkSync(tempReportPath);
1941
+ } catch {
1942
+ }
1943
+ } catch (err2) {
1944
+ console.error("[Checkly] Error in onEnd:", err2);
1268
1945
  }
1269
- if (this.testResults && this.testSession?.link) {
1270
- this.printSummary(report, this.testSession);
1271
- }
1272
- } catch (error) {
1273
- console.error("[Checkly Reporter] ERROR creating report:", error);
1274
1946
  }
1947
+ };
1948
+ }
1949
+
1950
+ // src/reporter.ts
1951
+ var import_node_fs = require("fs");
1952
+ var import_node_path = require("path");
1953
+ function getPackageVersion2() {
1954
+ try {
1955
+ const packageJson = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(__dirname, "..", "package.json"), "utf-8"));
1956
+ return packageJson.version;
1957
+ } catch {
1958
+ return "unknown";
1275
1959
  }
1276
- printSummary(report, testSession) {
1277
- const rule = pluralRules.select(report.config.projects.length);
1278
- console.log("\n======================================================\n");
1279
- console.log(`\u{1F99D} Checkly reporter: ${pkgVersion}`);
1280
- console.log(`\u{1F3AD} Playwright: ${report.config.version}`);
1281
- console.log(`\u{1F4D4} ${projectForms[rule]}: ${report.config.projects.map(({ name }) => name).join(",")}`);
1282
- console.log(`\u{1F517} Test session URL: ${testSession.link}`);
1283
- console.log("\n======================================================");
1960
+ }
1961
+ var pluralRules = new Intl.PluralRules("en-US");
1962
+ var projectForms = {
1963
+ zero: "Project",
1964
+ one: "Project",
1965
+ two: "Projects",
1966
+ few: "Projects",
1967
+ many: "Projects",
1968
+ other: "Projects"
1969
+ };
1970
+ var BaseReporter = class {
1971
+ config;
1972
+ suite;
1973
+ startTime;
1974
+ options;
1975
+ verbose;
1976
+ globalErrors = [];
1977
+ tests = /* @__PURE__ */ new Map();
1978
+ expectedCount = 0;
1979
+ unexpectedCount = 0;
1980
+ flakyCount = 0;
1981
+ skippedCount = 0;
1982
+ _report = null;
1983
+ extensions = [];
1984
+ summaryLines = [];
1985
+ // Reconstructed projects for merge-reports scenarios
1986
+ reconstructedProjects = /* @__PURE__ */ new Map();
1987
+ constructor(options = {}) {
1988
+ this.options = options;
1989
+ this.verbose = options.verbose ?? process.env.CHECKLY_REPORTER_VERBOSE === "true";
1284
1990
  }
1285
- /**
1286
- * Extracts console messages and network requests from all captured traces
1287
- * Called before injecting data into the report
1288
- */
1289
- async extractDataFromTraces() {
1290
- const extractionPromises = [];
1291
- for (const [key, tracePath] of this.tracePathsMap.entries()) {
1292
- extractionPromises.push(
1293
- (async () => {
1294
- const reader = new TraceReader(tracePath);
1295
- if (!await reader.open()) return;
1296
- const messages = await reader.extractEvents('"type":"console"', toConsoleMessage);
1297
- if (messages.length > 0) {
1298
- this.consoleMessagesMap.set(key, messages);
1299
- }
1300
- const networkRequests = await reader.extractEvents('"type":"resource-snapshot"', toNetworkRequest);
1301
- if (networkRequests.length > 0) {
1302
- this.networkRequestsMap.set(key, networkRequests);
1303
- }
1304
- })().catch((error) => {
1305
- console.error(`[Checkly Reporter] Failed to extract data from trace: ${error}`);
1306
- })
1307
- );
1991
+ use(extension) {
1992
+ this.extensions.push(extension);
1993
+ return this;
1994
+ }
1995
+ log(message, data) {
1996
+ if (!this.verbose) return;
1997
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[1].slice(0, 12);
1998
+ const prefix = `[Checkly ${timestamp}]`;
1999
+ if (data && Object.keys(data).length > 0) {
2000
+ console.log(`${prefix} ${message}`, data);
2001
+ } else {
2002
+ console.log(`${prefix} ${message}`);
1308
2003
  }
1309
- await Promise.all(extractionPromises);
1310
2004
  }
1311
- /**
1312
- * Injects captured steps, warnings, console messages, and network requests into the JSON report
1313
- * Traverses the report structure and matches by test ID + retry
1314
- */
1315
- injectDataIntoReport(report) {
1316
- const processSuite = (suite) => {
1317
- for (const spec of suite.specs) {
1318
- for (const test of spec.tests) {
1319
- for (const result of test.results) {
1320
- const key = `${spec.id}:${result.retry}`;
1321
- const steps = this.stepsMap.get(key);
1322
- if (steps) {
1323
- result.steps = steps;
1324
- }
1325
- const warnings = this.warningsMap.get(key);
1326
- const consoleMessages = this.consoleMessagesMap.get(key);
1327
- const networkRequests = this.networkRequestsMap.get(key);
1328
- const hasData = warnings && warnings.length > 0 || consoleMessages && consoleMessages.length > 0 || networkRequests && networkRequests.length > 0;
1329
- if (hasData) {
1330
- result._checkly = {
1331
- ...warnings && warnings.length > 0 ? { warnings } : {},
1332
- ...consoleMessages && consoleMessages.length > 0 ? { console: consoleMessages } : {},
1333
- ...networkRequests && networkRequests.length > 0 ? { network: networkRequests } : {}
1334
- };
1335
- }
1336
- }
1337
- }
1338
- }
1339
- if (suite.suites) {
1340
- for (const nestedSuite of suite.suites) {
1341
- processSuite(nestedSuite);
1342
- }
1343
- }
2005
+ createExtensionLogger(extensionName) {
2006
+ return (message, data) => {
2007
+ this.log(`[${extensionName}] ${message}`, data);
1344
2008
  };
1345
- for (const suite of report.suites) {
1346
- processSuite(suite);
1347
- }
1348
- this.reconstructProjectsFromTests(report);
1349
2009
  }
1350
- /**
1351
- * Reconstructs config.projects and test.projectId from test data
1352
- * This is necessary for blob merge scenarios where Playwright's JSON reporter
1353
- * doesn't populate projects array or projectId fields
1354
- */
1355
- reconstructProjectsFromTests(report) {
1356
- const projectNames = /* @__PURE__ */ new Set();
1357
- let testsWithMissingProjectId = 0;
1358
- const configAny = report.config;
1359
- const originalProjectsCount = configAny.projects?.length ?? 0;
1360
- this.log("reconstructProjectsFromTests started", {
1361
- originalProjectsCount,
1362
- suitesCount: report.suites?.length ?? 0
1363
- });
1364
- const collectProjectNames = (suite) => {
1365
- for (const spec of suite.specs) {
1366
- for (const test of spec.tests) {
1367
- const testAny = test;
1368
- if (testAny.projectName) {
1369
- projectNames.add(testAny.projectName);
1370
- }
1371
- if (testAny.projectName && !testAny.projectId) {
1372
- testAny.projectId = testAny.projectName;
1373
- testsWithMissingProjectId++;
1374
- }
1375
- }
2010
+ onBegin(config, suite) {
2011
+ const deprecated = ["outputFile", "testResultsDir", "outputPath"];
2012
+ const opts = this.options;
2013
+ for (const key of deprecated) {
2014
+ if (key in opts && opts[key] !== void 0) {
2015
+ throw new Error(`[Checkly] "${key}" option is no longer supported. Use outputDir instead.`);
1376
2016
  }
1377
- if (suite.suites) {
1378
- for (const nestedSuite of suite.suites) {
1379
- collectProjectNames(nestedSuite);
1380
- }
2017
+ }
2018
+ this.config = config;
2019
+ this.suite = suite;
2020
+ this.startTime = /* @__PURE__ */ new Date();
2021
+ this.tests.clear();
2022
+ this.globalErrors = [];
2023
+ this.expectedCount = 0;
2024
+ this.unexpectedCount = 0;
2025
+ this.flakyCount = 0;
2026
+ this.skippedCount = 0;
2027
+ this._report = null;
2028
+ this.reconstructedProjects.clear();
2029
+ this.reconstructProjectsIfNeeded();
2030
+ const testCount = this.countTests(suite);
2031
+ const projectNames = config.projects.length > 0 ? config.projects.map((p) => p.name).join(", ") : Array.from(this.reconstructedProjects.keys()).join(", ") || "default";
2032
+ this.log(`\u{1F3AC} Starting test run`, { tests: testCount, projects: projectNames, workers: config.workers });
2033
+ for (const ext of this.extensions) {
2034
+ ext.onBegin?.({ config, suite, log: this.createExtensionLogger(ext.name) });
2035
+ }
2036
+ }
2037
+ onTestBegin(test, result) {
2038
+ for (const ext of this.extensions) {
2039
+ ext.onTestBegin?.({ test, result, log: this.createExtensionLogger(ext.name) });
2040
+ }
2041
+ }
2042
+ onStepBegin(test, result, step) {
2043
+ for (const ext of this.extensions) {
2044
+ ext.onStepBegin?.({ test, result, step, log: this.createExtensionLogger(ext.name) });
2045
+ }
2046
+ }
2047
+ onStepEnd(test, result, step) {
2048
+ for (const ext of this.extensions) {
2049
+ ext.onStepEnd?.({ test, result, step, log: this.createExtensionLogger(ext.name) });
2050
+ }
2051
+ }
2052
+ onTestEnd(test, result) {
2053
+ const testId = test.id;
2054
+ let testData = this.tests.get(testId);
2055
+ if (!testData) {
2056
+ testData = { testCase: test, results: [] };
2057
+ this.tests.set(testId, testData);
2058
+ }
2059
+ testData.results.push(result);
2060
+ for (const ext of this.extensions) {
2061
+ ext.onTestEnd?.({ test, result, log: this.createExtensionLogger(ext.name) });
2062
+ }
2063
+ }
2064
+ onStdOut(chunk, test, result) {
2065
+ for (const ext of this.extensions) {
2066
+ ext.onStdOut?.({ chunk, test, result, log: this.createExtensionLogger(ext.name) });
2067
+ }
2068
+ }
2069
+ onStdErr(chunk, test, result) {
2070
+ for (const ext of this.extensions) {
2071
+ ext.onStdErr?.({ chunk, test, result, log: this.createExtensionLogger(ext.name) });
2072
+ }
2073
+ }
2074
+ onError(error) {
2075
+ this.globalErrors.push(error);
2076
+ this.log(`\u{1F4A5} Global error`, { message: error.message?.slice(0, 100) });
2077
+ for (const ext of this.extensions) {
2078
+ ext.onError?.({ error, log: this.createExtensionLogger(ext.name) });
2079
+ }
2080
+ }
2081
+ async onEnd(result) {
2082
+ for (const testData of this.tests.values()) {
2083
+ switch (testData.testCase.outcome()) {
2084
+ case "expected":
2085
+ this.expectedCount++;
2086
+ break;
2087
+ case "unexpected":
2088
+ this.unexpectedCount++;
2089
+ break;
2090
+ case "flaky":
2091
+ this.flakyCount++;
2092
+ break;
2093
+ case "skipped":
2094
+ this.skippedCount++;
2095
+ break;
1381
2096
  }
1382
- };
1383
- for (const suite of report.suites) {
1384
- collectProjectNames(suite);
1385
2097
  }
1386
- this.log("Project names collected from tests", {
1387
- uniqueProjectNames: Array.from(projectNames),
1388
- testsWithMissingProjectId
2098
+ this._report = this.buildReport(result);
2099
+ this.log(`\u{1F3C1} Test run finished`, {
2100
+ status: result.status,
2101
+ duration: `${result.duration}ms`,
2102
+ passed: this.expectedCount,
2103
+ failed: this.unexpectedCount,
2104
+ flaky: this.flakyCount,
2105
+ skipped: this.skippedCount
1389
2106
  });
1390
- if ((!configAny.projects || configAny.projects.length === 0) && projectNames.size > 0) {
1391
- configAny.projects = Array.from(projectNames).map((name) => ({
1392
- id: name,
1393
- name
1394
- }));
1395
- this.log("Reconstructed config.projects", {
1396
- reconstructedProjectsCount: configAny.projects.length,
1397
- projects: configAny.projects
1398
- });
1399
- } else {
1400
- this.log("No project reconstruction needed", {
1401
- reason: configAny.projects?.length > 0 ? "projects already present" : "no project names found in tests"
2107
+ const outputDir = this.options.outputDir;
2108
+ if (outputDir) {
2109
+ const fs6 = await import("fs");
2110
+ fs6.mkdirSync(outputDir, { recursive: true });
2111
+ const outputPath = `${outputDir}/checkly-report.json`;
2112
+ fs6.writeFileSync(outputPath, JSON.stringify(this._report, null, 2));
2113
+ this.log(`\u{1F4C4} Report saved`, { path: outputPath });
2114
+ }
2115
+ for (const ext of this.extensions) {
2116
+ await ext.onEnd?.({
2117
+ result,
2118
+ report: this._report,
2119
+ log: this.createExtensionLogger(ext.name),
2120
+ addSummaryLine: (line) => this.summaryLines.push(line)
1402
2121
  });
1403
2122
  }
2123
+ this.printSummary();
1404
2124
  }
1405
- /**
1406
- * Uploads test results to Checkly API
1407
- */
1408
- async uploadResults(_report, zipPath, entries) {
1409
- this.log("uploadResults started", {
1410
- zipPath,
1411
- entriesCount: entries.length,
1412
- hasTestResults: !!this.testResults,
1413
- hasTestSession: !!this.testSession
2125
+ async onExit() {
2126
+ this.log(`\u{1F44B} Reporter exiting`);
2127
+ for (const ext of this.extensions) {
2128
+ await ext.onExit?.({ log: this.createExtensionLogger(ext.name) });
2129
+ }
2130
+ }
2131
+ printsToStdio() {
2132
+ return false;
2133
+ }
2134
+ getReport() {
2135
+ if (this._report) return this._report;
2136
+ return this.buildReport({
2137
+ status: "passed",
2138
+ startTime: this.startTime,
2139
+ duration: Date.now() - this.startTime.getTime()
1414
2140
  });
1415
- if (!this.testResults || !this.testSession) {
1416
- this.log("uploadResults skipped - missing testResults or testSession");
2141
+ }
2142
+ buildReport(fullResult) {
2143
+ return {
2144
+ config: this.serializeConfig(),
2145
+ suites: this.serializeSuites(),
2146
+ errors: this.globalErrors.map((e) => this.serializeError(e, true)),
2147
+ stats: {
2148
+ startTime: this.startTime.toISOString(),
2149
+ duration: fullResult.duration,
2150
+ expected: this.expectedCount,
2151
+ unexpected: this.unexpectedCount,
2152
+ flaky: this.flakyCount,
2153
+ skipped: this.skippedCount
2154
+ }
2155
+ };
2156
+ }
2157
+ reconstructProjectsIfNeeded() {
2158
+ if (this.config.projects.length > 0) {
1417
2159
  return;
1418
2160
  }
1419
- try {
1420
- const { failed: failedCount, flaky: flakyCount } = this.testCounts;
1421
- const overallStatus = failedCount > 0 ? "FAILED" : "PASSED";
1422
- const isDegraded = failedCount === 0 && flakyCount > 0;
1423
- const endTime = /* @__PURE__ */ new Date();
1424
- const responseTime = this.startTime ? Math.max(0, endTime.getTime() - this.startTime.getTime()) : 0;
1425
- const zipSizeBytes = (await fs4.promises.stat(zipPath)).size;
1426
- this.log("Upload metadata calculated", {
1427
- testCounts: this.testCounts,
1428
- overallStatus,
1429
- isDegraded,
1430
- responseTime,
1431
- zipSizeBytes,
1432
- testSessionId: this.testSession.testSessionId,
1433
- testResultsCount: this.testSession.testResults.length
1434
- });
1435
- if (this.testSession.testResults.length > 0) {
1436
- const firstResult = this.testSession.testResults[0];
1437
- this.log("Using first test result for upload", {
1438
- testResultId: firstResult.testResultId
1439
- });
1440
- let assetId;
1441
- if (zipSizeBytes > 0) {
1442
- this.log("Starting S3 asset upload", { zipSizeBytes });
1443
- try {
1444
- const assets = fs4.createReadStream(zipPath);
1445
- const uploadResponse = await this.testResults.uploadTestResultAsset(
1446
- this.testSession.testSessionId,
1447
- firstResult.testResultId,
1448
- assets
1449
- );
1450
- assetId = uploadResponse.assetId;
1451
- this.log("S3 asset upload completed", { assetId });
1452
- } catch (error) {
1453
- const errorMessage = error instanceof Error ? error.message : String(error);
1454
- console.error("[Checkly Reporter] Asset upload failed:", errorMessage);
1455
- this.log("S3 asset upload failed", { error: errorMessage });
1456
- }
2161
+ this.log("\u{1F504} Detected merge-reports scenario (empty config.projects), reconstructing projects");
2162
+ for (const projectSuite of this.suite.suites) {
2163
+ const project = projectSuite.project();
2164
+ if (project) {
2165
+ const name = project.name || "default";
2166
+ if (!this.reconstructedProjects.has(name)) {
2167
+ this.reconstructedProjects.set(name, {
2168
+ id: name,
2169
+ name,
2170
+ testDir: project.testDir,
2171
+ outputDir: project.outputDir,
2172
+ timeout: project.timeout,
2173
+ retries: project.retries,
2174
+ repeatEach: project.repeatEach,
2175
+ metadata: project.metadata ?? {},
2176
+ testMatch: Array.isArray(project.testMatch) ? project.testMatch.map(String) : [String(project.testMatch)],
2177
+ testIgnore: Array.isArray(project.testIgnore) ? project.testIgnore.map(String) : [String(project.testIgnore)]
2178
+ });
2179
+ }
2180
+ }
2181
+ }
2182
+ if (this.reconstructedProjects.size === 0) {
2183
+ this.collectProjectsFromTests(this.suite);
2184
+ }
2185
+ this.log("\u{1F504} Reconstructed projects", {
2186
+ count: this.reconstructedProjects.size,
2187
+ names: Array.from(this.reconstructedProjects.keys())
2188
+ });
2189
+ }
2190
+ collectProjectsFromTests(suite) {
2191
+ for (const test of suite.allTests()) {
2192
+ const project = test.parent.project();
2193
+ const projectName = project?.name || "default";
2194
+ if (!this.reconstructedProjects.has(projectName)) {
2195
+ if (project) {
2196
+ this.reconstructedProjects.set(projectName, {
2197
+ id: projectName,
2198
+ name: projectName,
2199
+ testDir: project.testDir,
2200
+ outputDir: project.outputDir,
2201
+ timeout: project.timeout,
2202
+ retries: project.retries,
2203
+ repeatEach: project.repeatEach,
2204
+ metadata: project.metadata ?? {},
2205
+ testMatch: Array.isArray(project.testMatch) ? project.testMatch.map(String) : [String(project.testMatch)],
2206
+ testIgnore: Array.isArray(project.testIgnore) ? project.testIgnore.map(String) : [String(project.testIgnore)]
2207
+ });
1457
2208
  } else {
1458
- this.log("Skipping S3 upload - ZIP is empty");
2209
+ this.reconstructedProjects.set(projectName, {
2210
+ id: projectName,
2211
+ name: projectName,
2212
+ testDir: "",
2213
+ outputDir: "",
2214
+ timeout: 0,
2215
+ retries: 0,
2216
+ repeatEach: 1,
2217
+ metadata: {},
2218
+ testMatch: [],
2219
+ testIgnore: []
2220
+ });
1459
2221
  }
1460
- const updatePayload = {
1461
- status: overallStatus,
1462
- assetEntries: assetId ? entries : void 0,
1463
- isDegraded,
1464
- startedAt: this.startTime?.toISOString(),
1465
- stoppedAt: endTime.toISOString(),
1466
- responseTime,
1467
- metadata: {
1468
- usageData: {
1469
- s3PostTotalBytes: zipSizeBytes
1470
- }
1471
- }
1472
- };
1473
- this.log("Updating test result", {
1474
- testResultId: firstResult.testResultId,
1475
- payload: updatePayload,
1476
- assetEntriesCount: assetId ? entries.length : 0
1477
- });
1478
- await this.testResults.updateTestResult(this.testSession.testSessionId, firstResult.testResultId, updatePayload);
1479
- this.log("Test result updated successfully");
2222
+ }
2223
+ }
2224
+ }
2225
+ serializeConfig() {
2226
+ const c = this.config;
2227
+ const projects = c.projects.length > 0 ? c.projects.map((p) => this.serializeProject(p)) : Array.from(this.reconstructedProjects.values());
2228
+ return {
2229
+ rootDir: c.rootDir,
2230
+ configFile: c.configFile ?? void 0,
2231
+ version: c.version,
2232
+ workers: c.workers,
2233
+ fullyParallel: c.fullyParallel,
2234
+ forbidOnly: c.forbidOnly,
2235
+ globalTimeout: c.globalTimeout,
2236
+ maxFailures: c.maxFailures,
2237
+ metadata: c.metadata ?? {},
2238
+ projects,
2239
+ shard: c.shard ?? null,
2240
+ tags: c.tags ?? [],
2241
+ updateSourceMethod: c.updateSourceMethod,
2242
+ preserveOutput: c.preserveOutput,
2243
+ quiet: c.quiet,
2244
+ reportSlowTests: c.reportSlowTests ?? null,
2245
+ webServer: c.webServer ?? null,
2246
+ globalSetup: c.globalSetup ?? null,
2247
+ globalTeardown: c.globalTeardown ?? null,
2248
+ grep: c.grep,
2249
+ grepInvert: c.grepInvert,
2250
+ reporter: c.reporter,
2251
+ updateSnapshots: c.updateSnapshots
2252
+ };
2253
+ }
2254
+ serializeProject(p) {
2255
+ return {
2256
+ id: p.name,
2257
+ name: p.name,
2258
+ testDir: p.testDir,
2259
+ outputDir: p.outputDir,
2260
+ timeout: p.timeout,
2261
+ retries: p.retries,
2262
+ repeatEach: p.repeatEach,
2263
+ metadata: p.metadata ?? {},
2264
+ testMatch: Array.isArray(p.testMatch) ? p.testMatch.map(String) : [String(p.testMatch)],
2265
+ testIgnore: Array.isArray(p.testIgnore) ? p.testIgnore.map(String) : [String(p.testIgnore)]
2266
+ };
2267
+ }
2268
+ serializeSuites() {
2269
+ const fileSuites = /* @__PURE__ */ new Map();
2270
+ for (const projectSuite of this.suite.suites) {
2271
+ for (const fileSuite of projectSuite.suites) {
2272
+ const file = fileSuite.location?.file;
2273
+ if (!file) continue;
2274
+ const fileName = this.getFileName(file);
2275
+ const serialized = this.serializeSuite(fileSuite, fileName);
2276
+ if (!serialized) continue;
2277
+ const existing = fileSuites.get(file);
2278
+ if (existing) {
2279
+ this.mergeSuites(existing, serialized);
2280
+ } else {
2281
+ fileSuites.set(file, serialized);
2282
+ }
2283
+ }
2284
+ }
2285
+ return Array.from(fileSuites.values());
2286
+ }
2287
+ serializeSuite(suite, fileName) {
2288
+ const allTests = suite.allTests();
2289
+ if (allTests.length === 0) return null;
2290
+ const childSuites = [];
2291
+ for (const child of suite.suites) {
2292
+ const serialized = this.serializeSuite(child, fileName);
2293
+ if (serialized) childSuites.push(serialized);
2294
+ }
2295
+ const specs = [];
2296
+ for (const test of suite.tests) {
2297
+ const testData = this.tests.get(test.id);
2298
+ if (testData) {
2299
+ specs.push(this.serializeSpec(testData));
2300
+ }
2301
+ }
2302
+ return {
2303
+ title: suite.title || fileName,
2304
+ file: fileName,
2305
+ line: suite.location?.line ?? 0,
2306
+ column: suite.location?.column ?? 0,
2307
+ specs,
2308
+ suites: childSuites.length ? childSuites : void 0
2309
+ };
2310
+ }
2311
+ mergeSuites(to, from) {
2312
+ for (const fromSuite of from.suites || []) {
2313
+ const toSuite = to.suites?.find(
2314
+ (s) => s.title === fromSuite.title && s.line === fromSuite.line && s.column === fromSuite.column
2315
+ );
2316
+ if (toSuite) {
2317
+ this.mergeSuites(toSuite, fromSuite);
1480
2318
  } else {
1481
- this.log("No test results in session to update");
2319
+ to.suites = to.suites ?? [];
2320
+ to.suites.push(fromSuite);
1482
2321
  }
1483
- } catch (error) {
1484
- const errorMessage = error instanceof Error ? error.message : String(error);
1485
- console.error("[Checkly Reporter] Failed to upload results:", errorMessage);
1486
- this.log("uploadResults failed", { error: errorMessage });
1487
2322
  }
2323
+ to.specs.push(...from.specs);
1488
2324
  }
1489
- /**
1490
- * Called when a global error occurs
1491
- */
1492
- onError(error) {
1493
- console.error("[Checkly Reporter] Global error:", error);
2325
+ serializeSpec(data) {
2326
+ const { testCase } = data;
2327
+ const outcome = testCase.outcome();
2328
+ return {
2329
+ id: testCase.id,
2330
+ title: testCase.title,
2331
+ file: this.getFileName(testCase.location.file),
2332
+ line: testCase.location.line,
2333
+ column: testCase.location.column,
2334
+ tags: this.extractTags(testCase),
2335
+ ok: outcome === "expected" || outcome === "flaky" || outcome === "skipped",
2336
+ tests: [this.serializeTest(data)]
2337
+ };
2338
+ }
2339
+ extractTags(testCase) {
2340
+ const runtimeTags = testCase.tags;
2341
+ if (runtimeTags && runtimeTags.length > 0) {
2342
+ return runtimeTags.map((t) => t.replace(/^@/, ""));
2343
+ }
2344
+ const titleTags = testCase.title.match(/@[\w-]+/g);
2345
+ return titleTags?.map((t) => t.replace(/^@/, "")) ?? [];
2346
+ }
2347
+ serializeTest(data) {
2348
+ const { testCase, results } = data;
2349
+ const projectName = this.getProjectName(testCase);
2350
+ return {
2351
+ projectId: projectName,
2352
+ projectName,
2353
+ timeout: testCase.timeout,
2354
+ expectedStatus: testCase.expectedStatus,
2355
+ annotations: this.serializeAnnotations(testCase.annotations),
2356
+ results: results.map((r) => this.serializeTestResult(r)),
2357
+ status: this.mapOutcome(testCase.outcome())
2358
+ };
2359
+ }
2360
+ getProjectName(testCase) {
2361
+ const project = testCase.parent.project();
2362
+ if (project && typeof project.name === "string") {
2363
+ return project.name;
2364
+ }
2365
+ let current = testCase.parent;
2366
+ while (current) {
2367
+ const suiteProject = current.project();
2368
+ if (suiteProject && typeof suiteProject.name === "string") {
2369
+ return suiteProject.name;
2370
+ }
2371
+ current = current.parent;
2372
+ }
2373
+ return "default";
2374
+ }
2375
+ serializeTestResult(r) {
2376
+ return {
2377
+ workerIndex: r.workerIndex,
2378
+ parallelIndex: r.parallelIndex ?? 0,
2379
+ status: r.status,
2380
+ duration: r.duration,
2381
+ startTime: r.startTime.toISOString(),
2382
+ retry: r.retry,
2383
+ errors: r.errors.map((e) => this.serializeError(e, true)),
2384
+ error: r.error ? this.serializeError(r.error, false) : void 0,
2385
+ errorLocation: r.error?.location,
2386
+ stdout: r.stdout.map(
2387
+ (s) => typeof s === "string" ? { text: s } : Buffer.isBuffer(s) ? { buffer: s.toString("base64") } : s
2388
+ ),
2389
+ stderr: r.stderr.map(
2390
+ (s) => typeof s === "string" ? { text: s } : Buffer.isBuffer(s) ? { buffer: s.toString("base64") } : s
2391
+ ),
2392
+ attachments: r.attachments.map((a) => ({
2393
+ name: a.name,
2394
+ contentType: a.contentType,
2395
+ path: a.path,
2396
+ body: a.body ? a.body.toString("base64") : void 0
2397
+ })),
2398
+ steps: r.steps.map((s) => this.serializeStep(s)),
2399
+ annotations: this.serializeAnnotations(
2400
+ r.annotations ?? []
2401
+ )
2402
+ };
2403
+ }
2404
+ serializeStep(step) {
2405
+ return {
2406
+ title: step.title,
2407
+ duration: step.duration,
2408
+ error: step.error ? this.serializeError(step.error, false) : void 0,
2409
+ steps: step.steps?.map((s) => this.serializeStep(s))
2410
+ };
2411
+ }
2412
+ serializeError(error, includeContext) {
2413
+ if (error.value !== void 0) {
2414
+ return { message: error.value, value: error.value };
2415
+ }
2416
+ let msg = error.message ?? "Unknown error";
2417
+ if (includeContext && !/^(\w+Error|Error):/.test(msg)) {
2418
+ msg = "Error: " + msg;
2419
+ }
2420
+ if (includeContext) {
2421
+ if (error.snippet) msg += "\n\n" + error.snippet;
2422
+ if (error.stack) {
2423
+ const frames = error.stack.split("\n").filter((l) => l.trim().startsWith("at "));
2424
+ if (frames.length) msg += "\n" + frames.join("\n");
2425
+ } else if (error.location) {
2426
+ msg += `
2427
+ at ${error.location.file}:${error.location.line}:${error.location.column}`;
2428
+ }
2429
+ }
2430
+ return {
2431
+ message: msg,
2432
+ location: error.location,
2433
+ stack: error.stack,
2434
+ snippet: error.snippet
2435
+ };
2436
+ }
2437
+ serializeAnnotations(annotations) {
2438
+ return annotations.map((a) => ({ type: a.type, description: a.description, location: a.location }));
2439
+ }
2440
+ countTests(suite) {
2441
+ let count = suite.tests?.length ?? 0;
2442
+ for (const child of suite.suites ?? []) {
2443
+ count += this.countTests(child);
2444
+ }
2445
+ return count;
2446
+ }
2447
+ getFileName(filePath) {
2448
+ return filePath.split("/").pop() ?? filePath;
2449
+ }
2450
+ mapOutcome(outcome) {
2451
+ if (outcome === "expected" || outcome === "unexpected" || outcome === "flaky" || outcome === "skipped") {
2452
+ return outcome;
2453
+ }
2454
+ return "unexpected";
2455
+ }
2456
+ printSummary() {
2457
+ const pkgVersion = getPackageVersion2();
2458
+ const playwrightVersion = this.config.version;
2459
+ const projectNames = this.config.projects.length > 0 ? this.config.projects.map((p) => p.name).join(", ") : Array.from(this.reconstructedProjects.keys()).join(", ") || "default";
2460
+ const projectCount = this.config.projects.length > 0 ? this.config.projects.length : this.reconstructedProjects.size || 1;
2461
+ const rule = pluralRules.select(projectCount);
2462
+ console.log("\n======================================================\n");
2463
+ console.log(`\u{1F99D} Checkly reporter: ${pkgVersion}`);
2464
+ console.log(`\u{1F3AD} Playwright: ${playwrightVersion}`);
2465
+ console.log(`\u{1F4D4} ${projectForms[rule]}: ${projectNames}`);
2466
+ for (const line of this.summaryLines) {
2467
+ console.log(line);
2468
+ }
2469
+ console.log("\n======================================================");
1494
2470
  }
1495
2471
  };
1496
- export {
1497
- AssetCollector,
1498
- ChecklyReporter,
1499
- Zipper,
1500
- ChecklyReporter as default
2472
+
2473
+ // src/index.ts
2474
+ var ChecklyReporter = class extends BaseReporter {
2475
+ constructor(options = {}) {
2476
+ super(options);
2477
+ this.use(checklyUpload(options));
2478
+ }
1501
2479
  };
2480
+ var index_default = ChecklyReporter;
2481
+ function createChecklyReporter(options = {}) {
2482
+ return ["@checkly/playwright-reporter", options];
2483
+ }
2484
+ // Annotate the CommonJS export names for ESM import in node:
2485
+ 0 && (module.exports = {
2486
+ MissingCredentialsError,
2487
+ createChecklyReporter
2488
+ });