@reportforge/playwright-pdf 0.3.1 → 0.4.1

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/demo/cli.js CHANGED
@@ -1219,13 +1219,13 @@ var require_ast = __commonJS({
1219
1219
  helperExpression: function helperExpression(node) {
1220
1220
  return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
1221
1221
  },
1222
- scopedId: function scopedId(path8) {
1223
- return /^\.|this\b/.test(path8.original);
1222
+ scopedId: function scopedId(path11) {
1223
+ return /^\.|this\b/.test(path11.original);
1224
1224
  },
1225
1225
  // an ID is simple if it only has one part, and that part is not
1226
1226
  // `..` or `this`.
1227
- simpleId: function simpleId(path8) {
1228
- return path8.parts.length === 1 && !AST.helpers.scopedId(path8) && !path8.depth;
1227
+ simpleId: function simpleId(path11) {
1228
+ return path11.parts.length === 1 && !AST.helpers.scopedId(path11) && !path11.depth;
1229
1229
  }
1230
1230
  }
1231
1231
  };
@@ -2299,12 +2299,12 @@ var require_helpers2 = __commonJS({
2299
2299
  loc
2300
2300
  };
2301
2301
  }
2302
- function prepareMustache(path8, params, hash, open, strip, locInfo) {
2302
+ function prepareMustache(path11, params, hash, open, strip, locInfo) {
2303
2303
  var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
2304
2304
  var decorator = /\*/.test(open);
2305
2305
  return {
2306
2306
  type: decorator ? "Decorator" : "MustacheStatement",
2307
- path: path8,
2307
+ path: path11,
2308
2308
  params,
2309
2309
  hash,
2310
2310
  escaped,
@@ -2624,9 +2624,9 @@ var require_compiler = __commonJS({
2624
2624
  },
2625
2625
  DecoratorBlock: function DecoratorBlock(decorator) {
2626
2626
  var program = decorator.program && this.compileProgram(decorator.program);
2627
- var params = this.setupFullMustacheParams(decorator, program, void 0), path8 = decorator.path;
2627
+ var params = this.setupFullMustacheParams(decorator, program, void 0), path11 = decorator.path;
2628
2628
  this.useDecorators = true;
2629
- this.opcode("registerDecorator", params.length, path8.original);
2629
+ this.opcode("registerDecorator", params.length, path11.original);
2630
2630
  },
2631
2631
  PartialStatement: function PartialStatement(partial) {
2632
2632
  this.usePartial = true;
@@ -2690,46 +2690,46 @@ var require_compiler = __commonJS({
2690
2690
  }
2691
2691
  },
2692
2692
  ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
2693
- var path8 = sexpr.path, name = path8.parts[0], isBlock = program != null || inverse != null;
2694
- this.opcode("getContext", path8.depth);
2693
+ var path11 = sexpr.path, name = path11.parts[0], isBlock = program != null || inverse != null;
2694
+ this.opcode("getContext", path11.depth);
2695
2695
  this.opcode("pushProgram", program);
2696
2696
  this.opcode("pushProgram", inverse);
2697
- path8.strict = true;
2698
- this.accept(path8);
2697
+ path11.strict = true;
2698
+ this.accept(path11);
2699
2699
  this.opcode("invokeAmbiguous", name, isBlock);
2700
2700
  },
2701
2701
  simpleSexpr: function simpleSexpr(sexpr) {
2702
- var path8 = sexpr.path;
2703
- path8.strict = true;
2704
- this.accept(path8);
2702
+ var path11 = sexpr.path;
2703
+ path11.strict = true;
2704
+ this.accept(path11);
2705
2705
  this.opcode("resolvePossibleLambda");
2706
2706
  },
2707
2707
  helperSexpr: function helperSexpr(sexpr, program, inverse) {
2708
- var params = this.setupFullMustacheParams(sexpr, program, inverse), path8 = sexpr.path, name = path8.parts[0];
2708
+ var params = this.setupFullMustacheParams(sexpr, program, inverse), path11 = sexpr.path, name = path11.parts[0];
2709
2709
  if (this.options.knownHelpers[name]) {
2710
2710
  this.opcode("invokeKnownHelper", params.length, name);
2711
2711
  } else if (this.options.knownHelpersOnly) {
2712
2712
  throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
2713
2713
  } else {
2714
- path8.strict = true;
2715
- path8.falsy = true;
2716
- this.accept(path8);
2717
- this.opcode("invokeHelper", params.length, path8.original, _ast2["default"].helpers.simpleId(path8));
2714
+ path11.strict = true;
2715
+ path11.falsy = true;
2716
+ this.accept(path11);
2717
+ this.opcode("invokeHelper", params.length, path11.original, _ast2["default"].helpers.simpleId(path11));
2718
2718
  }
2719
2719
  },
2720
- PathExpression: function PathExpression(path8) {
2721
- this.addDepth(path8.depth);
2722
- this.opcode("getContext", path8.depth);
2723
- var name = path8.parts[0], scoped = _ast2["default"].helpers.scopedId(path8), blockParamId = !path8.depth && !scoped && this.blockParamIndex(name);
2720
+ PathExpression: function PathExpression(path11) {
2721
+ this.addDepth(path11.depth);
2722
+ this.opcode("getContext", path11.depth);
2723
+ var name = path11.parts[0], scoped = _ast2["default"].helpers.scopedId(path11), blockParamId = !path11.depth && !scoped && this.blockParamIndex(name);
2724
2724
  if (blockParamId) {
2725
- this.opcode("lookupBlockParam", blockParamId, path8.parts);
2725
+ this.opcode("lookupBlockParam", blockParamId, path11.parts);
2726
2726
  } else if (!name) {
2727
2727
  this.opcode("pushContext");
2728
- } else if (path8.data) {
2728
+ } else if (path11.data) {
2729
2729
  this.options.data = true;
2730
- this.opcode("lookupData", path8.depth, path8.parts, path8.strict);
2730
+ this.opcode("lookupData", path11.depth, path11.parts, path11.strict);
2731
2731
  } else {
2732
- this.opcode("lookupOnContext", path8.parts, path8.falsy, path8.strict, scoped);
2732
+ this.opcode("lookupOnContext", path11.parts, path11.falsy, path11.strict, scoped);
2733
2733
  }
2734
2734
  },
2735
2735
  StringLiteral: function StringLiteral(string) {
@@ -3085,16 +3085,16 @@ var require_util = __commonJS({
3085
3085
  }
3086
3086
  exports2.urlGenerate = urlGenerate;
3087
3087
  function normalize(aPath) {
3088
- var path8 = aPath;
3088
+ var path11 = aPath;
3089
3089
  var url = urlParse(aPath);
3090
3090
  if (url) {
3091
3091
  if (!url.path) {
3092
3092
  return aPath;
3093
3093
  }
3094
- path8 = url.path;
3094
+ path11 = url.path;
3095
3095
  }
3096
- var isAbsolute = exports2.isAbsolute(path8);
3097
- var parts = path8.split(/\/+/);
3096
+ var isAbsolute = exports2.isAbsolute(path11);
3097
+ var parts = path11.split(/\/+/);
3098
3098
  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
3099
3099
  part = parts[i];
3100
3100
  if (part === ".") {
@@ -3111,15 +3111,15 @@ var require_util = __commonJS({
3111
3111
  }
3112
3112
  }
3113
3113
  }
3114
- path8 = parts.join("/");
3115
- if (path8 === "") {
3116
- path8 = isAbsolute ? "/" : ".";
3114
+ path11 = parts.join("/");
3115
+ if (path11 === "") {
3116
+ path11 = isAbsolute ? "/" : ".";
3117
3117
  }
3118
3118
  if (url) {
3119
- url.path = path8;
3119
+ url.path = path11;
3120
3120
  return urlGenerate(url);
3121
3121
  }
3122
- return path8;
3122
+ return path11;
3123
3123
  }
3124
3124
  exports2.normalize = normalize;
3125
3125
  function join(aRoot, aPath) {
@@ -5922,8 +5922,8 @@ var require_printer = __commonJS({
5922
5922
  return this.accept(sexpr.path) + " " + params + hash;
5923
5923
  };
5924
5924
  PrintVisitor.prototype.PathExpression = function(id) {
5925
- var path8 = id.parts.join("/");
5926
- return (id.data ? "@" : "") + "PATH:" + path8;
5925
+ var path11 = id.parts.join("/");
5926
+ return (id.data ? "@" : "") + "PATH:" + path11;
5927
5927
  };
5928
5928
  PrintVisitor.prototype.StringLiteral = function(string) {
5929
5929
  return '"' + string.value + '"';
@@ -5964,8 +5964,8 @@ var require_lib = __commonJS({
5964
5964
  handlebars.print = printer.print;
5965
5965
  module2.exports = handlebars;
5966
5966
  function extension(module3, filename) {
5967
- var fs9 = require("fs");
5968
- var templateString = fs9.readFileSync(filename, "utf8");
5967
+ var fs11 = require("fs");
5968
+ var templateString = fs11.readFileSync(filename, "utf8");
5969
5969
  module3.exports = handlebars.compile(templateString);
5970
5970
  }
5971
5971
  if (typeof require !== "undefined" && require.extensions) {
@@ -5994,7 +5994,7 @@ var require_util2 = __commonJS({
5994
5994
  "node_modules/chrome-finder/lib/util.js"(exports2, module2) {
5995
5995
  "use strict";
5996
5996
  init_cjs_shims();
5997
- var fs9 = require("fs");
5997
+ var fs11 = require("fs");
5998
5998
  var newLineRegex = /\r?\n/;
5999
5999
  function sort(installations, priorities) {
6000
6000
  const defaultPriority = 10;
@@ -6012,7 +6012,7 @@ var require_util2 = __commonJS({
6012
6012
  return false;
6013
6013
  }
6014
6014
  try {
6015
- fs9.accessSync(file);
6015
+ fs11.accessSync(file);
6016
6016
  return true;
6017
6017
  } catch (e) {
6018
6018
  return false;
@@ -6023,7 +6023,7 @@ var require_util2 = __commonJS({
6023
6023
  return false;
6024
6024
  }
6025
6025
  try {
6026
- var stat = fs9.statSync(file);
6026
+ var stat = fs11.statSync(file);
6027
6027
  return stat && typeof stat.isFile === "function" && stat.isFile();
6028
6028
  } catch (e) {
6029
6029
  return false;
@@ -6043,7 +6043,7 @@ var require_win32 = __commonJS({
6043
6043
  "node_modules/chrome-finder/lib/win32.js"(exports2, module2) {
6044
6044
  "use strict";
6045
6045
  init_cjs_shims();
6046
- var path8 = require("path");
6046
+ var path11 = require("path");
6047
6047
  var { canAccess } = require_util2();
6048
6048
  function win32() {
6049
6049
  const installations = [];
@@ -6057,7 +6057,7 @@ var require_win32 = __commonJS({
6057
6057
  const prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"]];
6058
6058
  prefixes.forEach((prefix) => suffixes.forEach((suffix) => {
6059
6059
  if (prefix) {
6060
- const chromePath = path8.join(prefix, suffix);
6060
+ const chromePath = path11.join(prefix, suffix);
6061
6061
  if (canAccess(chromePath)) {
6062
6062
  installations.push(chromePath);
6063
6063
  }
@@ -6075,7 +6075,7 @@ var require_darwin = __commonJS({
6075
6075
  "use strict";
6076
6076
  init_cjs_shims();
6077
6077
  var { execSync: execSync2 } = require("child_process");
6078
- var path8 = require("path");
6078
+ var path11 = require("path");
6079
6079
  var { canAccess, newLineRegex, sort } = require_util2();
6080
6080
  function darwin() {
6081
6081
  const suffixes = ["/Contents/MacOS/Google Chrome Canary", "/Contents/MacOS/Google Chrome", "/Contents/MacOS/Chromium"];
@@ -6085,7 +6085,7 @@ var require_darwin = __commonJS({
6085
6085
  ${LSREGISTER} -dump | grep -E -i -o '/.+(google chrome( canary)?|chromium)\\.app(\\s|$)' | grep -E -v 'Caches|TimeMachine|Temporary|/Volumes|\\.Trash'
6086
6086
  `).toString().split(newLineRegex).forEach((inst) => {
6087
6087
  suffixes.forEach((suffix) => {
6088
- const execPath = path8.join(inst.trim(), suffix);
6088
+ const execPath = path11.join(inst.trim(), suffix);
6089
6089
  if (canAccess(execPath)) {
6090
6090
  installations.push(execPath);
6091
6091
  }
@@ -6114,8 +6114,8 @@ var require_linux = __commonJS({
6114
6114
  "use strict";
6115
6115
  init_cjs_shims();
6116
6116
  var { execSync: execSync2, execFileSync: execFileSync2 } = require("child_process");
6117
- var path8 = require("path");
6118
- var fs9 = require("fs");
6117
+ var path11 = require("path");
6118
+ var fs11 = require("fs");
6119
6119
  var { canAccess, sort, isExecutable, newLineRegex } = require_util2();
6120
6120
  function findChromeExecutablesForLinuxDesktop(folder) {
6121
6121
  const argumentsRegex = /(^[^ ]+).*/;
@@ -6132,7 +6132,7 @@ var require_linux = __commonJS({
6132
6132
  function linux() {
6133
6133
  let installations = [];
6134
6134
  const desktopInstallationFolders = [
6135
- path8.join(require("os").homedir(), ".local/share/applications/"),
6135
+ path11.join(require("os").homedir(), ".local/share/applications/"),
6136
6136
  "/usr/share/applications/"
6137
6137
  ];
6138
6138
  desktopInstallationFolders.forEach((folder) => {
@@ -6158,7 +6158,7 @@ var require_linux = __commonJS({
6158
6158
  ].map((possiblePath) => {
6159
6159
  try {
6160
6160
  const chromePathToTest = possiblePath + "/" + executable;
6161
- if (fs9.existsSync(chromePathToTest) && canAccess(chromePathToTest) && isExecutable(chromePathToTest)) {
6161
+ if (fs11.existsSync(chromePathToTest) && canAccess(chromePathToTest) && isExecutable(chromePathToTest)) {
6162
6162
  installations.push(chromePathToTest);
6163
6163
  return chromePathToTest;
6164
6164
  }
@@ -6494,6 +6494,42 @@ function formatDuration(ms) {
6494
6494
  return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
6495
6495
  }
6496
6496
 
6497
+ // src/analysis/SummaryWriter.ts
6498
+ init_cjs_shims();
6499
+ var CATEGORY_LABEL = {
6500
+ assertion: "Assertion failure",
6501
+ timeout: "Timeout",
6502
+ "locator-not-found": "Element not found",
6503
+ network: "Network error",
6504
+ navigation: "Navigation error",
6505
+ "env-config": "Environment / config",
6506
+ "flaky-by-retry": "Flaky (passed on retry)",
6507
+ unknown: "Unclassified"
6508
+ };
6509
+ var SHORT_LABEL = {
6510
+ assertion: "assertion",
6511
+ timeout: "timeout",
6512
+ "locator-not-found": "element not found",
6513
+ network: "network",
6514
+ navigation: "navigation",
6515
+ "env-config": "env/config",
6516
+ "flaky-by-retry": "flaky",
6517
+ unknown: "unclassified"
6518
+ };
6519
+ function buildOneLiner(clusters) {
6520
+ if (clusters.length === 0) return "";
6521
+ const byCategory = /* @__PURE__ */ new Map();
6522
+ for (const c of clusters) {
6523
+ byCategory.set(c.category, (byCategory.get(c.category) ?? 0) + c.count);
6524
+ }
6525
+ const head = `${clusters.length} root ${clusters.length === 1 ? "cause" : "causes"}`;
6526
+ const tally = [...byCategory.entries()].sort((a, b) => b[1] - a[1]).map(([cat, count]) => `${count} ${SHORT_LABEL[cat]}`);
6527
+ return [head, ...tally].join(" \xB7 ");
6528
+ }
6529
+ function dominantCategory(clusters) {
6530
+ return clusters[0]?.category ?? "unknown";
6531
+ }
6532
+
6497
6533
  // src/templates/engine.ts
6498
6534
  var TemplateEngine = class {
6499
6535
  constructor() {
@@ -6720,6 +6756,10 @@ var TemplateEngine = class {
6720
6756
  );
6721
6757
  this.handlebars.registerHelper("lt", (a, b) => a < b);
6722
6758
  this.handlebars.registerHelper("or", (a, b) => Boolean(a) || Boolean(b));
6759
+ this.handlebars.registerHelper("categoryLabel", (cat) => CATEGORY_LABEL[cat] ?? cat);
6760
+ this.handlebars.registerHelper("strengthLabel", (s) => s ? s.charAt(0).toUpperCase() + s.slice(1) : "");
6761
+ this.handlebars.registerHelper("pluralise", (n, one, many) => n === 1 ? one : many);
6762
+ this.handlebars.registerHelper("subtract", (a, b) => (a ?? 0) - (b ?? 0));
6723
6763
  this.handlebars.registerHelper("statusBadge", (status) => {
6724
6764
  const map = {
6725
6765
  passed: "badge--passed",
@@ -7133,6 +7173,7 @@ init_cjs_shims();
7133
7173
 
7134
7174
  // src/license/pubkey-bundle.ts
7135
7175
  init_cjs_shims();
7176
+ var LICENSE_PUBLIC_KEY_PEM = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAbvknstk0gxoJ7UwATdlxR8btxvO7u7RdpO1uV90PtuE=\n-----END PUBLIC KEY-----";
7136
7177
 
7137
7178
  // src/pdf/PdfGenerator.ts
7138
7179
  var PdfGenerator = class {
@@ -7181,11 +7222,11 @@ var PdfGenerator = class {
7181
7222
  if (!licenseInfo.jwt) {
7182
7223
  throw new Error("[reportforge] License JWT missing \u2014 PDF generation skipped.");
7183
7224
  }
7184
- if (!LICENSE_PUBLIC_KEY_PEM) {
7225
+ if (!LICENSE_PUBLIC_KEY_PEM2) {
7185
7226
  throw new Error("[reportforge] LICENSE_PUBLIC_KEY_PEM missing \u2014 cannot verify license JWT.");
7186
7227
  }
7187
7228
  try {
7188
- const payload = new JwtVerifier(LICENSE_PUBLIC_KEY_PEM).verify(licenseInfo.jwt);
7229
+ const payload = new JwtVerifier(LICENSE_PUBLIC_KEY_PEM2).verify(licenseInfo.jwt);
7189
7230
  if (payload.exp * 1e3 <= Date.now()) {
7190
7231
  throw new Error("JWT expired");
7191
7232
  }
@@ -7843,8 +7884,8 @@ function getErrorMap() {
7843
7884
  // node_modules/zod/v3/helpers/parseUtil.js
7844
7885
  init_cjs_shims();
7845
7886
  var makeIssue = (params) => {
7846
- const { data, path: path8, errorMaps, issueData } = params;
7847
- const fullPath = [...path8, ...issueData.path || []];
7887
+ const { data, path: path11, errorMaps, issueData } = params;
7888
+ const fullPath = [...path11, ...issueData.path || []];
7848
7889
  const fullIssue = {
7849
7890
  ...issueData,
7850
7891
  path: fullPath
@@ -7964,11 +8005,11 @@ var errorUtil;
7964
8005
 
7965
8006
  // node_modules/zod/v3/types.js
7966
8007
  var ParseInputLazyPath = class {
7967
- constructor(parent, value, path8, key) {
8008
+ constructor(parent, value, path11, key) {
7968
8009
  this._cachedPath = [];
7969
8010
  this.parent = parent;
7970
8011
  this.data = value;
7971
- this._path = path8;
8012
+ this._path = path11;
7972
8013
  this._key = key;
7973
8014
  }
7974
8015
  get path() {
@@ -11448,6 +11489,16 @@ var notifyConfigSchema = external_exports.object({
11448
11489
  discord: channelConfigSchema.optional(),
11449
11490
  email: emailChannelConfigSchema.optional()
11450
11491
  }).optional();
11492
+ var failureAnalysisConfigSchema = external_exports.object({
11493
+ enabled: external_exports.boolean().default(true),
11494
+ maxClusters: external_exports.number().int().min(1).max(50).default(10),
11495
+ // Qualitative floor (not a numeric % — consistent with the match-strength design).
11496
+ minStrength: external_exports.enum(["weak", "moderate", "strong"]).default("weak"),
11497
+ maxFailuresToAnalyse: external_exports.number().int().min(1).default(500),
11498
+ // Consumed by the Plan 3 feedback collector; inert until then.
11499
+ collectUnclassified: external_exports.boolean().default(true),
11500
+ autoUpdateModel: external_exports.boolean().default(true)
11501
+ }).default({});
11451
11502
  var ReporterOptionsSchema = external_exports.object({
11452
11503
  outputFile: external_exports.string().optional().default(DEFAULT_OPTIONS.outputFile),
11453
11504
  template: external_exports.union([
@@ -11477,6 +11528,7 @@ var ReporterOptionsSchema = external_exports.object({
11477
11528
  historySize: external_exports.number().int().min(2).optional().default(DEFAULT_OPTIONS.historySize),
11478
11529
  showTrend: external_exports.boolean().optional().default(DEFAULT_OPTIONS.showTrend),
11479
11530
  flakinessTopN: external_exports.number().int().min(0, "flakinessTopN must be 0 or greater").optional().default(DEFAULT_OPTIONS.flakinessTopN),
11531
+ failureAnalysis: failureAnalysisConfigSchema,
11480
11532
  templatePath: external_exports.union([
11481
11533
  external_exports.string().regex(/\.hbs$/, "templatePath must end with .hbs").refine((v) => v.slice(0, -4).trim().length > 0, "templatePath basename must not be empty"),
11482
11534
  external_exports.array(
@@ -11500,6 +11552,307 @@ ${issues}`);
11500
11552
  init_cjs_shims();
11501
11553
  var TEMPLATE_IDS = ["minimal", "detailed", "executive"];
11502
11554
 
11555
+ // src/analysis/index.ts
11556
+ init_cjs_shims();
11557
+
11558
+ // src/analysis/ModelStore.ts
11559
+ init_cjs_shims();
11560
+ var import_node_fs = __toESM(require("fs"));
11561
+ var import_node_os = __toESM(require("os"));
11562
+ var import_node_path = __toESM(require("path"));
11563
+ var import_node_crypto = require("crypto");
11564
+
11565
+ // src/analysis/model-bundle.ts
11566
+ init_cjs_shims();
11567
+ var MODEL = { "alpha": 1, "labels": ["assertion", "env-config", "flaky-by-retry", "locator-not-found", "navigation", "network", "timeout"], "logLikelihoods": { "assertion": { "NUM": -4.574710978503383, "assertionerror": -4.980176086611547, "be": -4.980176086611547, "cart": -4.980176086611547, "checkout": -4.980176086611547, "com": -4.980176086611547, "dashboard": -4.980176086611547, "deep": -4.980176086611547, "equal": -4.980176086611547, "equality": -4.980176086611547, "error": -4.980176086611547, "expect": -3.5938817254916566, "expected": -3.1083739097099556, "failed": -4.980176086611547, "false": -4.980176086611547, "got": -4.980176086611547, "id": -4.574710978503383, "john": -4.980176086611547, "locator": -4.574710978503383, "login": -4.980176086611547, "must": -4.980176086611547, "page": -4.980176086611547, "pattern": -4.980176086611547, "received": -3.275427994373122, "string": -4.980176086611547, "to": -4.980176086611547, "tobe": -4.980176086611547, "tobetruthy": -4.980176086611547, "toequal": -4.980176086611547, "tohavecount": -4.980176086611547, "tohavetext": -4.980176086611547, "tohavetitle": -4.980176086611547, "tohavevalue": -4.980176086611547, "truthy": -4.980176086611547, "value": -4.980176086611547 }, "env-config": { "NUM": -4.997212273764115, "api": -4.997212273764115, "at": -4.997212273764115, "back": -4.997212273764115, "base": -4.997212273764115, "baseurl": -4.997212273764115, "browsers": -4.997212273764115, "cannot": -4.997212273764115, "ci": -4.997212273764115, "config": -4.304065093204169, "directory": -4.591747165655951, "dotenv": -4.997212273764115, "enoent": -4.997212273764115, "env": -4.591747165655951, "environment": -4.591747165655951, "error": -4.08092154188996, "existent": -4.997212273764115, "failed": -4.997212273764115, "falling": -4.997212273764115, "file": -4.997212273764115, "find": -4.997212273764115, "from": -4.997212273764115, "in": -4.591747165655951, "invalid": -4.997212273764115, "is": -4.997212273764115, "json": -4.591747165655951, "key": -4.997212273764115, "load": -4.997212273764115, "missing": -4.997212273764115, "module": -4.997212273764115, "no": -4.997212273764115, "non": -4.997212273764115, "not": -4.997212273764115, "open": -4.997212273764115, "or": -4.997212273764115, "path": -4.997212273764115, "playwright": -4.591747165655951, "points": -4.997212273764115, "position": -4.997212273764115, "required": -4.997212273764115, "set": -4.997212273764115, "spec": -4.997212273764115, "such": -4.997212273764115, "syntaxerror": -4.997212273764115, "test": -4.997212273764115, "to": -4.304065093204169, "token": -4.997212273764115, "ts": -4.997212273764115, "tsconfig": -4.997212273764115, "undefined": -4.591747165655951, "unexpected": -4.997212273764115, "url": -4.997212273764115, "values": -4.997212273764115, "variable": -4.591747165655951 }, "flaky-by-retry": { "after": -4.58836306767171, "and": -4.993828175779875, "animation": -4.993828175779875, "attempt": -4.07753744390572, "between": -4.993828175779875, "blip": -4.993828175779875, "cause": -4.993828175779875, "change": -4.993828175779875, "click": -4.993828175779875, "code": -4.993828175779875, "commit": -4.993828175779875, "condition": -4.993828175779875, "dependency": -4.993828175779875, "detached": -4.993828175779875, "deterministic": -4.993828175779875, "element": -4.993828175779875, "failed": -4.300680995219929, "fails": -4.993828175779875, "flaky": -4.07753744390572, "green": -4.993828175779875, "in": -4.993828175779875, "intermittent": -4.993828175779875, "locator": -4.993828175779875, "marked": -4.993828175779875, "network": -4.993828175779875, "no": -4.993828175779875, "non": -4.993828175779875, "on": -3.895215887111765, "order": -4.993828175779875, "passed": -3.895215887111765, "quarantined": -4.993828175779875, "race": -4.993828175779875, "reproducible": -4.993828175779875, "rerun": -4.993828175779875, "retry": -4.07753744390572, "runs": -4.993828175779875, "same": -4.993828175779875, "second": -4.993828175779875, "stale": -4.993828175779875, "succeeded": -4.993828175779875, "test": -4.58836306767171, "the": -4.993828175779875, "then": -4.993828175779875, "timeout": -4.993828175779875, "unstable": -4.993828175779875, "with": -4.993828175779875, "without": -4.993828175779875 }, "locator-not-found": { "an": -4.993828175779875, "attached": -4.993828175779875, "btn": -4.993828175779875, "button": -4.58836306767171, "cart": -4.993828175779875, "checkout": -4.993828175779875, "click": -4.993828175779875, "data": -4.993828175779875, "div": -4.993828175779875, "dom": -4.993828175779875, "element": -4.07753744390572, "elements": -4.300680995219929, "error": -4.58836306767171, "find": -4.993828175779875, "for": -4.58836306767171, "found": -4.58836306767171, "getbyrole": -4.993828175779875, "getbytext": -4.993828175779875, "icon": -4.993828175779875, "id": -4.993828175779875, "is": -4.993828175779875, "locator": -4.07753744390572, "matched": -4.993828175779875, "matches": -4.993828175779875, "missing": -4.993828175779875, "mode": -4.58836306767171, "name": -4.993828175779875, "no": -4.58836306767171, "node": -4.993828175779875, "not": -4.58836306767171, "resolved": -4.58836306767171, "save": -4.993828175779875, "selector": -4.58836306767171, "strict": -4.58836306767171, "submit": -4.993828175779875, "testid": -4.993828175779875, "textcontent": -4.993828175779875, "the": -4.58836306767171, "to": -4.07753744390572, "total": -4.993828175779875, "unable": -4.993828175779875, "violation": -4.58836306767171, "waiting": -4.993828175779875, "with": -4.993828175779875, "xpath": -4.993828175779875 }, "navigation": { "NUM": -4.983606621708336, "aborted": -4.983606621708336, "another": -4.983606621708336, "back": -4.983606621708336, "because": -4.578141513600172, "by": -4.578141513600172, "checkout": -4.983606621708336, "context": -4.983606621708336, "crashed": -4.983606621708336, "dashboard": -4.983606621708336, "destroyed": -4.983606621708336, "detached": -4.983606621708336, "entry": -4.983606621708336, "err": -4.578141513600172, "exceeded": -4.983606621708336, "execution": -4.983606621708336, "failed": -4.983606621708336, "frame": -4.578141513600172, "goback": -4.983606621708336, "goto": -4.290459441148391, "history": -4.983606621708336, "home": -4.983606621708336, "interrupted": -4.578141513600172, "is": -4.983606621708336, "likely": -4.983606621708336, "login": -4.983606621708336, "many": -4.983606621708336, "maybe": -4.983606621708336, "most": -4.983606621708336, "navigate": -4.983606621708336, "navigating": -4.983606621708336, "navigation": -3.7308436532129683, "net": -4.578141513600172, "no": -4.983606621708336, "of": -4.983606621708336, "page": -3.884994333040227, "redirects": -4.983606621708336, "timeout": -4.983606621708336, "to": -3.7308436532129683, "too": -4.983606621708336, "waitforurl": -4.983606621708336, "was": -4.578141513600172 }, "network": { "NUM": -4.57126863431241, "URL": -4.283586561860629, "again": -4.976733742420574, "api": -4.976733742420574, "apirequestcontext": -4.976733742420574, "at": -4.976733742420574, "body": -4.976733742420574, "connect": -4.976733742420574, "connection": -4.57126863431241, "disconnected": -4.976733742420574, "eai": -4.976733742420574, "econnrefused": -4.976733742420574, "err": -3.8781214537524646, "error": -4.976733742420574, "failed": -4.283586561860629, "fetch": -4.976733742420574, "fetching": -4.976733742420574, "from": -4.976733742420574, "get": -4.976733742420574, "getaddrinfo": -4.976733742420574, "goto": -4.976733742420574, "hang": -4.976733742420574, "internet": -4.976733742420574, "loading": -4.976733742420574, "name": -4.976733742420574, "net": -3.8781214537524646, "not": -4.976733742420574, "on": -4.976733742420574, "out": -4.976733742420574, "page": -4.57126863431241, "products": -4.976733742420574, "protocol": -4.976733742420574, "reading": -4.976733742420574, "reason": -4.976733742420574, "refused": -4.976733742420574, "request": -4.57126863431241, "resolved": -4.976733742420574, "resource": -4.976733742420574, "response": -4.976733742420574, "route": -4.976733742420574, "socket": -4.976733742420574, "ssl": -4.976733742420574, "timed": -4.976733742420574, "to": -4.976733742420574, "up": -4.976733742420574, "upstream": -4.976733742420574, "while": -4.976733742420574 }, "timeout": { "NUM": -3.4965075614664802, "after": -5.000584958242754, "be": -4.59511985013459, "cart": -5.000584958242754, "click": -5.000584958242754, "element": -5.000584958242754, "event": -5.000584958242754, "exceeded": -3.6142905971228636, "expect": -5.000584958242754, "fill": -5.000584958242754, "for": -3.7478219897473863, "function": -5.000584958242754, "load": -5.000584958242754, "locator": -4.084294226368599, "navigation": -5.000584958242754, "of": -5.000584958242754, "out": -5.000584958242754, "page": -4.59511985013459, "running": -5.000584958242754, "selector": -5.000584958242754, "submit": -5.000584958242754, "test": -4.59511985013459, "timed": -5.000584958242754, "timeout": -3.6142905971228636, "timeouterror": -4.59511985013459, "to": -4.59511985013459, "tobevisible": -5.000584958242754, "visible": -4.59511985013459, "waitforloadstate": -5.000584958242754, "waitforselector": -5.000584958242754, "waiting": -3.7478219897473863, "while": -5.000584958242754 } }, "playwrightVersion": "1.59.1", "priors": { "assertion": -1.9636097261547143, "env-config": -1.9636097261547143, "flaky-by-retry": -1.9636097261547143, "locator-not-found": -1.9636097261547143, "navigation": -1.9636097261547143, "network": -1.9636097261547143, "timeout": -1.845826690498331 }, "totals": { "assertion": 64, "env-config": 69, "flaky-by-retry": 68, "locator-not-found": 68, "navigation": 65, "network": 63, "timeout": 70 }, "version": 1, "vocabSize": 227 };
11568
+
11569
+ // src/analysis/ModelStore.ts
11570
+ var MODEL_SIG_DOMAIN = "reportforge-model-sig:v1\n";
11571
+ var testDeps = null;
11572
+ function cacheFile() {
11573
+ return testDeps?.cacheFile ?? import_node_path.default.join(import_node_os.default.homedir(), ".reportforge", "model.json");
11574
+ }
11575
+ function publicKey() {
11576
+ const pem = testDeps?.publicKeyPem ?? LICENSE_PUBLIC_KEY_PEM;
11577
+ try {
11578
+ return pem && pem.includes("PUBLIC KEY") ? (0, import_node_crypto.createPublicKey)(pem) : null;
11579
+ } catch {
11580
+ return null;
11581
+ }
11582
+ }
11583
+ function verifyModelSignature(modelJson, signatureB64url) {
11584
+ const key = publicKey();
11585
+ if (!key) return false;
11586
+ try {
11587
+ return (0, import_node_crypto.verify)(null, Buffer.from(MODEL_SIG_DOMAIN + modelJson), key, Buffer.from(signatureB64url, "base64url"));
11588
+ } catch {
11589
+ return false;
11590
+ }
11591
+ }
11592
+ function schemaValid(m) {
11593
+ const x = m;
11594
+ return !!x && Array.isArray(x.labels) && x.labels.length > 0 && typeof x.version === "number" && !!x.priors && !!x.totals && !!x.logLikelihoods && typeof x.vocabSize === "number" && typeof x.alpha === "number";
11595
+ }
11596
+ function readCache() {
11597
+ try {
11598
+ const raw = import_node_fs.default.readFileSync(cacheFile(), "utf8");
11599
+ const c = JSON.parse(raw);
11600
+ if (typeof c.version === "number" && typeof c.modelJson === "string" && typeof c.signature === "string") {
11601
+ return c;
11602
+ }
11603
+ } catch {
11604
+ }
11605
+ return null;
11606
+ }
11607
+ function loadModel(autoUpdate) {
11608
+ if (!autoUpdate) return MODEL;
11609
+ const c = readCache();
11610
+ if (!c || c.version <= MODEL.version) return MODEL;
11611
+ if (!verifyModelSignature(c.modelJson, c.signature)) {
11612
+ logger.debug(`cached model v${c.version} signature invalid \u2014 using bundled (possible tampering or key mismatch)`);
11613
+ return MODEL;
11614
+ }
11615
+ try {
11616
+ const m = JSON.parse(c.modelJson);
11617
+ if (schemaValid(m)) return m;
11618
+ logger.debug(`cached model v${c.version} failed schema validation \u2014 using bundled`);
11619
+ } catch {
11620
+ logger.debug(`cached model v${c.version} JSON parse failed \u2014 using bundled`);
11621
+ }
11622
+ return MODEL;
11623
+ }
11624
+
11625
+ // src/analysis/FailureClusterer.ts
11626
+ init_cjs_shims();
11627
+
11628
+ // src/analysis/NaiveBayesClassifier.ts
11629
+ init_cjs_shims();
11630
+
11631
+ // src/analysis/tokenize.ts
11632
+ init_cjs_shims();
11633
+ function tokenize(text) {
11634
+ return text.toLowerCase().replace(/https?:\/\/\S+/gi, " URL ").replace(/[^a-zA-Z0-9\s]/g, " ").replace(/\b\d{3,}[a-z]*\b/g, " NUM ").split(/\s+/).filter((t) => t.length > 1);
11635
+ }
11636
+
11637
+ // src/analysis/NaiveBayesClassifier.ts
11638
+ function classify(model, text) {
11639
+ const tokens = tokenize(text);
11640
+ const vocabTokens = tokens.filter(
11641
+ (tok) => model.labels.some((l) => model.logLikelihoods[l]?.[tok] !== void 0)
11642
+ );
11643
+ if (vocabTokens.length === 0) {
11644
+ return { label: "unknown", margin: 0 };
11645
+ }
11646
+ const scores = {};
11647
+ for (const label of model.labels) {
11648
+ let s = model.priors[label];
11649
+ const oov = Math.log(model.alpha / (model.totals[label] + model.alpha * model.vocabSize));
11650
+ const ll = model.logLikelihoods[label];
11651
+ for (const tok of vocabTokens) {
11652
+ s += ll[tok] ?? oov;
11653
+ }
11654
+ scores[label] = s;
11655
+ }
11656
+ const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
11657
+ const topLog = ranked[0][1];
11658
+ const secondLog = ranked[1]?.[1] ?? topLog;
11659
+ const margin = (topLog - secondLog) / vocabTokens.length;
11660
+ return { label: ranked[0][0], margin };
11661
+ }
11662
+
11663
+ // src/analysis/constants.ts
11664
+ init_cjs_shims();
11665
+ var STRENGTH = {
11666
+ unknownMaxMargin: 0.05,
11667
+ collectMaxMargin: 0.1,
11668
+ harvestMinMargin: 0.25,
11669
+ moderateMinMargin: 0.1,
11670
+ strongMinMargin: 0.25
11671
+ };
11672
+ var STRENGTH_LEVELS = ["weak", "moderate", "strong"];
11673
+ var FLAKY_MARGIN = 999;
11674
+
11675
+ // src/analysis/FailureClusterer.ts
11676
+ var MAX_TESTS_PER_CLUSTER = 20;
11677
+ var MAX_MESSAGE_CHARS = 200;
11678
+ function strengthOf(margin) {
11679
+ if (margin >= STRENGTH.strongMinMargin) return "strong";
11680
+ if (margin >= STRENGTH.moderateMinMargin) return "moderate";
11681
+ return "weak";
11682
+ }
11683
+ function classifyInput(f) {
11684
+ const head = (f.error.stack ?? "").split("\n").slice(0, 3).join(" ");
11685
+ return `${f.error.message} ${head}`;
11686
+ }
11687
+ function topFrame(stack) {
11688
+ if (!stack) return void 0;
11689
+ for (const line of stack.split("\n")) {
11690
+ const t = line.trim();
11691
+ if (t.startsWith("at ") && !t.replace(/\\/g, "/").includes("node_modules/playwright")) return t;
11692
+ }
11693
+ return void 0;
11694
+ }
11695
+ function representativeMessage(message) {
11696
+ const firstLine = message.split("\n")[0];
11697
+ return firstLine.length > MAX_MESSAGE_CHARS ? `${firstLine.slice(0, MAX_MESSAGE_CHARS)}\u2026` : firstLine;
11698
+ }
11699
+ function classifyOne(f, model) {
11700
+ if (f.retryHistory.some((r) => r.status === "passed")) {
11701
+ return { category: "flaky-by-retry", margin: FLAKY_MARGIN, failure: f, frame: topFrame(f.error.stack) };
11702
+ }
11703
+ const { label, margin } = classify(model, classifyInput(f));
11704
+ return { category: label, margin, failure: f, frame: topFrame(f.error.stack) };
11705
+ }
11706
+ function classifyAll(failures, model) {
11707
+ return failures.map((f) => classifyOne(f, model));
11708
+ }
11709
+ function clusterClassified(classified) {
11710
+ const groups = /* @__PURE__ */ new Map();
11711
+ for (const c of classified) {
11712
+ const key = `${c.category}::${c.frame ?? "__no-stack__"}`;
11713
+ const bucket = groups.get(key);
11714
+ if (bucket) bucket.push(c);
11715
+ else groups.set(key, [c]);
11716
+ }
11717
+ const clusters = [];
11718
+ for (const members of groups.values()) {
11719
+ const margin = Math.max(...members.map((m) => m.margin));
11720
+ clusters.push({
11721
+ category: members[0].category,
11722
+ strength: strengthOf(margin),
11723
+ margin,
11724
+ count: members.length,
11725
+ tests: members.slice(0, MAX_TESTS_PER_CLUSTER).map((m) => m.failure.testTitle),
11726
+ testsTotal: members.length,
11727
+ representativeMessage: representativeMessage(members[0].failure.error.message),
11728
+ stackFrame: members[0].frame
11729
+ });
11730
+ }
11731
+ return clusters;
11732
+ }
11733
+
11734
+ // src/analysis/UnclassifiedCollector.ts
11735
+ init_cjs_shims();
11736
+ var import_node_fs2 = __toESM(require("fs"));
11737
+ var import_node_os2 = __toESM(require("os"));
11738
+ var import_node_path2 = __toESM(require("path"));
11739
+
11740
+ // src/analysis/feedback-csv.ts
11741
+ init_cjs_shims();
11742
+ var FEEDBACK_HEADER = "category,margin,text";
11743
+ function parseFeedbackRows(content) {
11744
+ return content.trim().split("\n").slice(1).filter(Boolean).map((line) => {
11745
+ const parts = line.split(",");
11746
+ return { category: parts[0], margin: parts[1], text: parts.slice(2).join(","), raw: line };
11747
+ });
11748
+ }
11749
+ function feedbackIdentity(category, text) {
11750
+ return `${category} ${text}`;
11751
+ }
11752
+
11753
+ // src/analysis/UnclassifiedCollector.ts
11754
+ var MAX_ROWS = 500;
11755
+ var FILENAME = "unclassified.csv";
11756
+ var URI_RE = /[a-z][\w+.-]*:\/\/\S+/gi;
11757
+ var EMAIL_RE = /\b[\w.+-]+@[\w.-]+\.\w+\b/g;
11758
+ var SECRET_RE = /\b(?=[A-Za-z0-9_+/=-]*[0-9])(?=[A-Za-z0-9_+/=-]*[A-Za-z])[A-Za-z0-9_+/=-]{16,}\b/g;
11759
+ var IP_RE = /\b\d{1,3}(?:\.\d{1,3}){3}\b/g;
11760
+ var WIN_PATH_RE = /[A-Za-z]:\\[^\s'"]+/g;
11761
+ var UNIX_PATH_RE = /(?:\/[\w.-]+){2,}/g;
11762
+ function redactForStorage(raw) {
11763
+ const redacted = raw.replace(URI_RE, " URL ").replace(EMAIL_RE, " EMAIL ").replace(SECRET_RE, " SECRET ").replace(IP_RE, " IP ").replace(WIN_PATH_RE, " PATH ").replace(UNIX_PATH_RE, " PATH ");
11764
+ return tokenize(redacted).join(" ");
11765
+ }
11766
+ function shouldCollect(c) {
11767
+ return c.category === "unknown" || c.margin <= STRENGTH.collectMaxMargin;
11768
+ }
11769
+ function tildeify(p) {
11770
+ const home = import_node_os2.default.homedir();
11771
+ return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
11772
+ }
11773
+ function collectUnclassified(classified, projectDir) {
11774
+ const rows = classified.filter(shouldCollect).map((c) => ({ text: redactForStorage(c.failure.error.message), category: c.category, margin: c.margin })).filter((r) => r.text.length > 0);
11775
+ if (rows.length === 0) return;
11776
+ const file = import_node_path2.default.join(projectDir, FILENAME);
11777
+ const firstTime = !import_node_fs2.default.existsSync(file);
11778
+ const byIdentity = /* @__PURE__ */ new Map();
11779
+ if (!firstTime) {
11780
+ for (const r of parseFeedbackRows(import_node_fs2.default.readFileSync(file, "utf8"))) {
11781
+ byIdentity.set(feedbackIdentity(r.category, r.text), r.raw);
11782
+ }
11783
+ }
11784
+ let added = false;
11785
+ for (const r of rows) {
11786
+ const id = feedbackIdentity(r.category, r.text);
11787
+ byIdentity.delete(id);
11788
+ byIdentity.set(id, `${r.category},${r.margin},${r.text}`);
11789
+ added = true;
11790
+ }
11791
+ if (!added) return;
11792
+ const all = [...byIdentity.values()].slice(-MAX_ROWS);
11793
+ import_node_fs2.default.mkdirSync(projectDir, { recursive: true });
11794
+ import_node_fs2.default.writeFileSync(file, `${FEEDBACK_HEADER}
11795
+ ${all.join("\n")}
11796
+ `, "utf8");
11797
+ if (firstTime) {
11798
+ logger.info(
11799
+ `Unrecognized failures collected locally at ${tildeify(file)} to improve offline classification. Local only - never sent anywhere. Disable: failureAnalysis.collectUnclassified=false`
11800
+ );
11801
+ }
11802
+ }
11803
+
11804
+ // src/utils/paths.ts
11805
+ init_cjs_shims();
11806
+ var import_node_os3 = __toESM(require("os"));
11807
+ var import_node_path3 = __toESM(require("path"));
11808
+ var import_node_crypto2 = __toESM(require("crypto"));
11809
+ function resolveProjectDir(options, cwd) {
11810
+ const projectKey = import_node_crypto2.default.createHash("sha256").update(cwd + (options.outputFile ?? "")).digest("hex").slice(0, 8);
11811
+ return import_node_path3.default.join(import_node_os3.default.homedir(), ".reportforge", projectKey);
11812
+ }
11813
+
11814
+ // src/analysis/index.ts
11815
+ var DEFAULTS = { maxClusters: 10, minStrength: "weak", maxFailuresToAnalyse: 500 };
11816
+ var STRENGTH_RANK = Object.fromEntries(
11817
+ STRENGTH_LEVELS.map((level, i) => [level, i])
11818
+ );
11819
+ function analyseClassified(classified, totalFailures, opts = {}) {
11820
+ const maxClusters = opts.maxClusters ?? DEFAULTS.maxClusters;
11821
+ const minRank = STRENGTH_RANK[opts.minStrength ?? DEFAULTS.minStrength];
11822
+ const cap = opts.maxFailuresToAnalyse ?? DEFAULTS.maxFailuresToAnalyse;
11823
+ const clusters = clusterClassified(classified).filter((c) => STRENGTH_RANK[c.strength] >= minRank).sort((a, b) => b.count - a.count).slice(0, maxClusters);
11824
+ return {
11825
+ clusters,
11826
+ totalFailures,
11827
+ analysedCount: classified.length,
11828
+ truncated: totalFailures > cap,
11829
+ // dominantCategory is part of the AnalysisResult contract; reserved for
11830
+ // consumers (e.g. a future executive badge / notify summary). Not rendered
11831
+ // by the v1 partials.
11832
+ dominantCategory: dominantCategory(clusters),
11833
+ oneLiner: buildOneLiner(clusters)
11834
+ };
11835
+ }
11836
+ function templatesConsumeAnalysis(options) {
11837
+ if (options.templatePath && options.templatePath.length > 0) return true;
11838
+ const templates = Array.isArray(options.template) ? options.template : [options.template];
11839
+ return templates.some((t) => t === "detailed" || t === "executive");
11840
+ }
11841
+ function runFailureAnalysis(reportData, options, cwd) {
11842
+ const fa = options.failureAnalysis;
11843
+ if (reportData.stats.failed === 0 || !fa.enabled) return;
11844
+ const cap = fa.maxFailuresToAnalyse;
11845
+ const analysed = reportData.failures.slice(0, cap);
11846
+ const model = loadModel(fa.autoUpdateModel);
11847
+ const classified = classifyAll(analysed, model);
11848
+ if (templatesConsumeAnalysis(options)) {
11849
+ reportData.analysis = analyseClassified(classified, reportData.failures.length, fa);
11850
+ }
11851
+ if (fa.collectUnclassified) {
11852
+ collectUnclassified(classified, resolveProjectDir(options, cwd));
11853
+ }
11854
+ }
11855
+
11503
11856
  // src/demo/cli.ts
11504
11857
  var DEMO_LICENSE_INFO2 = {
11505
11858
  plan: "subscription",
@@ -11529,8 +11882,9 @@ async function run() {
11529
11882
  console.log(` Template : ${template}`);
11530
11883
  console.log(` Output : ${outputFile}`);
11531
11884
  const reportData = buildDemoReportData(template);
11532
- const options = parseOptions({ template, outputFile });
11885
+ const options = parseOptions({ template, outputFile, failureAnalysis: { collectUnclassified: false } });
11533
11886
  const generator = new PdfGenerator();
11887
+ runFailureAnalysis(reportData, options, process.cwd());
11534
11888
  const paths = await generator.generateAll(reportData, options, DEMO_LICENSE_INFO2);
11535
11889
  if (paths.length > 0) {
11536
11890
  console.log(`