@reportforge/playwright-pdf 0.22.0 → 0.23.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/README.md +68 -21
- package/SECURITY.md +13 -2
- package/dist/cli/index.js +228 -118
- package/dist/index.d.mts +31 -10
- package/dist/index.d.ts +31 -10
- package/dist/index.js +296 -117
- package/dist/index.mjs +290 -111
- package/package.json +2 -2
- package/CHANGELOG.md +0 -602
package/dist/index.mjs
CHANGED
|
@@ -1229,13 +1229,13 @@ var require_ast = __commonJS({
|
|
|
1229
1229
|
helperExpression: function helperExpression(node) {
|
|
1230
1230
|
return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
|
|
1231
1231
|
},
|
|
1232
|
-
scopedId: function scopedId(
|
|
1233
|
-
return /^\.|this\b/.test(
|
|
1232
|
+
scopedId: function scopedId(path16) {
|
|
1233
|
+
return /^\.|this\b/.test(path16.original);
|
|
1234
1234
|
},
|
|
1235
1235
|
// an ID is simple if it only has one part, and that part is not
|
|
1236
1236
|
// `..` or `this`.
|
|
1237
|
-
simpleId: function simpleId(
|
|
1238
|
-
return
|
|
1237
|
+
simpleId: function simpleId(path16) {
|
|
1238
|
+
return path16.parts.length === 1 && !AST.helpers.scopedId(path16) && !path16.depth;
|
|
1239
1239
|
}
|
|
1240
1240
|
}
|
|
1241
1241
|
};
|
|
@@ -2309,12 +2309,12 @@ var require_helpers2 = __commonJS({
|
|
|
2309
2309
|
loc
|
|
2310
2310
|
};
|
|
2311
2311
|
}
|
|
2312
|
-
function prepareMustache(
|
|
2312
|
+
function prepareMustache(path16, params, hash, open, strip, locInfo) {
|
|
2313
2313
|
var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
|
|
2314
2314
|
var decorator = /\*/.test(open);
|
|
2315
2315
|
return {
|
|
2316
2316
|
type: decorator ? "Decorator" : "MustacheStatement",
|
|
2317
|
-
path:
|
|
2317
|
+
path: path16,
|
|
2318
2318
|
params,
|
|
2319
2319
|
hash,
|
|
2320
2320
|
escaped,
|
|
@@ -2634,9 +2634,9 @@ var require_compiler = __commonJS({
|
|
|
2634
2634
|
},
|
|
2635
2635
|
DecoratorBlock: function DecoratorBlock(decorator) {
|
|
2636
2636
|
var program = decorator.program && this.compileProgram(decorator.program);
|
|
2637
|
-
var params = this.setupFullMustacheParams(decorator, program, void 0),
|
|
2637
|
+
var params = this.setupFullMustacheParams(decorator, program, void 0), path16 = decorator.path;
|
|
2638
2638
|
this.useDecorators = true;
|
|
2639
|
-
this.opcode("registerDecorator", params.length,
|
|
2639
|
+
this.opcode("registerDecorator", params.length, path16.original);
|
|
2640
2640
|
},
|
|
2641
2641
|
PartialStatement: function PartialStatement(partial) {
|
|
2642
2642
|
this.usePartial = true;
|
|
@@ -2700,46 +2700,46 @@ var require_compiler = __commonJS({
|
|
|
2700
2700
|
}
|
|
2701
2701
|
},
|
|
2702
2702
|
ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
|
|
2703
|
-
var
|
|
2704
|
-
this.opcode("getContext",
|
|
2703
|
+
var path16 = sexpr.path, name = path16.parts[0], isBlock = program != null || inverse != null;
|
|
2704
|
+
this.opcode("getContext", path16.depth);
|
|
2705
2705
|
this.opcode("pushProgram", program);
|
|
2706
2706
|
this.opcode("pushProgram", inverse);
|
|
2707
|
-
|
|
2708
|
-
this.accept(
|
|
2707
|
+
path16.strict = true;
|
|
2708
|
+
this.accept(path16);
|
|
2709
2709
|
this.opcode("invokeAmbiguous", name, isBlock);
|
|
2710
2710
|
},
|
|
2711
2711
|
simpleSexpr: function simpleSexpr(sexpr) {
|
|
2712
|
-
var
|
|
2713
|
-
|
|
2714
|
-
this.accept(
|
|
2712
|
+
var path16 = sexpr.path;
|
|
2713
|
+
path16.strict = true;
|
|
2714
|
+
this.accept(path16);
|
|
2715
2715
|
this.opcode("resolvePossibleLambda");
|
|
2716
2716
|
},
|
|
2717
2717
|
helperSexpr: function helperSexpr(sexpr, program, inverse) {
|
|
2718
|
-
var params = this.setupFullMustacheParams(sexpr, program, inverse),
|
|
2718
|
+
var params = this.setupFullMustacheParams(sexpr, program, inverse), path16 = sexpr.path, name = path16.parts[0];
|
|
2719
2719
|
if (this.options.knownHelpers[name]) {
|
|
2720
2720
|
this.opcode("invokeKnownHelper", params.length, name);
|
|
2721
2721
|
} else if (this.options.knownHelpersOnly) {
|
|
2722
2722
|
throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
|
|
2723
2723
|
} else {
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
this.accept(
|
|
2727
|
-
this.opcode("invokeHelper", params.length,
|
|
2724
|
+
path16.strict = true;
|
|
2725
|
+
path16.falsy = true;
|
|
2726
|
+
this.accept(path16);
|
|
2727
|
+
this.opcode("invokeHelper", params.length, path16.original, _ast2["default"].helpers.simpleId(path16));
|
|
2728
2728
|
}
|
|
2729
2729
|
},
|
|
2730
|
-
PathExpression: function PathExpression(
|
|
2731
|
-
this.addDepth(
|
|
2732
|
-
this.opcode("getContext",
|
|
2733
|
-
var name =
|
|
2730
|
+
PathExpression: function PathExpression(path16) {
|
|
2731
|
+
this.addDepth(path16.depth);
|
|
2732
|
+
this.opcode("getContext", path16.depth);
|
|
2733
|
+
var name = path16.parts[0], scoped = _ast2["default"].helpers.scopedId(path16), blockParamId = !path16.depth && !scoped && this.blockParamIndex(name);
|
|
2734
2734
|
if (blockParamId) {
|
|
2735
|
-
this.opcode("lookupBlockParam", blockParamId,
|
|
2735
|
+
this.opcode("lookupBlockParam", blockParamId, path16.parts);
|
|
2736
2736
|
} else if (!name) {
|
|
2737
2737
|
this.opcode("pushContext");
|
|
2738
|
-
} else if (
|
|
2738
|
+
} else if (path16.data) {
|
|
2739
2739
|
this.options.data = true;
|
|
2740
|
-
this.opcode("lookupData",
|
|
2740
|
+
this.opcode("lookupData", path16.depth, path16.parts, path16.strict);
|
|
2741
2741
|
} else {
|
|
2742
|
-
this.opcode("lookupOnContext",
|
|
2742
|
+
this.opcode("lookupOnContext", path16.parts, path16.falsy, path16.strict, scoped);
|
|
2743
2743
|
}
|
|
2744
2744
|
},
|
|
2745
2745
|
StringLiteral: function StringLiteral(string) {
|
|
@@ -3095,16 +3095,16 @@ var require_util = __commonJS({
|
|
|
3095
3095
|
}
|
|
3096
3096
|
exports.urlGenerate = urlGenerate;
|
|
3097
3097
|
function normalize(aPath) {
|
|
3098
|
-
var
|
|
3098
|
+
var path16 = aPath;
|
|
3099
3099
|
var url = urlParse(aPath);
|
|
3100
3100
|
if (url) {
|
|
3101
3101
|
if (!url.path) {
|
|
3102
3102
|
return aPath;
|
|
3103
3103
|
}
|
|
3104
|
-
|
|
3104
|
+
path16 = url.path;
|
|
3105
3105
|
}
|
|
3106
|
-
var isAbsolute = exports.isAbsolute(
|
|
3107
|
-
var parts =
|
|
3106
|
+
var isAbsolute = exports.isAbsolute(path16);
|
|
3107
|
+
var parts = path16.split(/\/+/);
|
|
3108
3108
|
for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
|
|
3109
3109
|
part = parts[i];
|
|
3110
3110
|
if (part === ".") {
|
|
@@ -3121,15 +3121,15 @@ var require_util = __commonJS({
|
|
|
3121
3121
|
}
|
|
3122
3122
|
}
|
|
3123
3123
|
}
|
|
3124
|
-
|
|
3125
|
-
if (
|
|
3126
|
-
|
|
3124
|
+
path16 = parts.join("/");
|
|
3125
|
+
if (path16 === "") {
|
|
3126
|
+
path16 = isAbsolute ? "/" : ".";
|
|
3127
3127
|
}
|
|
3128
3128
|
if (url) {
|
|
3129
|
-
url.path =
|
|
3129
|
+
url.path = path16;
|
|
3130
3130
|
return urlGenerate(url);
|
|
3131
3131
|
}
|
|
3132
|
-
return
|
|
3132
|
+
return path16;
|
|
3133
3133
|
}
|
|
3134
3134
|
exports.normalize = normalize;
|
|
3135
3135
|
function join(aRoot, aPath) {
|
|
@@ -5932,8 +5932,8 @@ var require_printer = __commonJS({
|
|
|
5932
5932
|
return this.accept(sexpr.path) + " " + params + hash;
|
|
5933
5933
|
};
|
|
5934
5934
|
PrintVisitor.prototype.PathExpression = function(id) {
|
|
5935
|
-
var
|
|
5936
|
-
return (id.data ? "@" : "") + "PATH:" +
|
|
5935
|
+
var path16 = id.parts.join("/");
|
|
5936
|
+
return (id.data ? "@" : "") + "PATH:" + path16;
|
|
5937
5937
|
};
|
|
5938
5938
|
PrintVisitor.prototype.StringLiteral = function(string) {
|
|
5939
5939
|
return '"' + string.value + '"';
|
|
@@ -6053,7 +6053,7 @@ var require_win32 = __commonJS({
|
|
|
6053
6053
|
"node_modules/chrome-finder/lib/win32.js"(exports, module) {
|
|
6054
6054
|
"use strict";
|
|
6055
6055
|
init_esm_shims();
|
|
6056
|
-
var
|
|
6056
|
+
var path16 = __require("path");
|
|
6057
6057
|
var { canAccess } = require_util2();
|
|
6058
6058
|
function win32() {
|
|
6059
6059
|
const installations = [];
|
|
@@ -6067,7 +6067,7 @@ var require_win32 = __commonJS({
|
|
|
6067
6067
|
const prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"]];
|
|
6068
6068
|
prefixes.forEach((prefix) => suffixes.forEach((suffix) => {
|
|
6069
6069
|
if (prefix) {
|
|
6070
|
-
const chromePath =
|
|
6070
|
+
const chromePath = path16.join(prefix, suffix);
|
|
6071
6071
|
if (canAccess(chromePath)) {
|
|
6072
6072
|
installations.push(chromePath);
|
|
6073
6073
|
}
|
|
@@ -6085,7 +6085,7 @@ var require_darwin = __commonJS({
|
|
|
6085
6085
|
"use strict";
|
|
6086
6086
|
init_esm_shims();
|
|
6087
6087
|
var { execSync: execSync3 } = __require("child_process");
|
|
6088
|
-
var
|
|
6088
|
+
var path16 = __require("path");
|
|
6089
6089
|
var { canAccess, newLineRegex, sort } = require_util2();
|
|
6090
6090
|
function darwin() {
|
|
6091
6091
|
const suffixes = ["/Contents/MacOS/Google Chrome Canary", "/Contents/MacOS/Google Chrome", "/Contents/MacOS/Chromium"];
|
|
@@ -6095,7 +6095,7 @@ var require_darwin = __commonJS({
|
|
|
6095
6095
|
${LSREGISTER} -dump | grep -E -i -o '/.+(google chrome( canary)?|chromium)\\.app(\\s|$)' | grep -E -v 'Caches|TimeMachine|Temporary|/Volumes|\\.Trash'
|
|
6096
6096
|
`).toString().split(newLineRegex).forEach((inst) => {
|
|
6097
6097
|
suffixes.forEach((suffix) => {
|
|
6098
|
-
const execPath =
|
|
6098
|
+
const execPath = path16.join(inst.trim(), suffix);
|
|
6099
6099
|
if (canAccess(execPath)) {
|
|
6100
6100
|
installations.push(execPath);
|
|
6101
6101
|
}
|
|
@@ -6123,8 +6123,8 @@ var require_linux = __commonJS({
|
|
|
6123
6123
|
"node_modules/chrome-finder/lib/linux.js"(exports, module) {
|
|
6124
6124
|
"use strict";
|
|
6125
6125
|
init_esm_shims();
|
|
6126
|
-
var { execSync: execSync3, execFileSync
|
|
6127
|
-
var
|
|
6126
|
+
var { execSync: execSync3, execFileSync } = __require("child_process");
|
|
6127
|
+
var path16 = __require("path");
|
|
6128
6128
|
var fs16 = __require("fs");
|
|
6129
6129
|
var { canAccess, sort, isExecutable, newLineRegex } = require_util2();
|
|
6130
6130
|
function findChromeExecutablesForLinuxDesktop(folder) {
|
|
@@ -6142,7 +6142,7 @@ var require_linux = __commonJS({
|
|
|
6142
6142
|
function linux() {
|
|
6143
6143
|
let installations = [];
|
|
6144
6144
|
const desktopInstallationFolders = [
|
|
6145
|
-
|
|
6145
|
+
path16.join(__require("os").homedir(), ".local/share/applications/"),
|
|
6146
6146
|
"/usr/share/applications/"
|
|
6147
6147
|
];
|
|
6148
6148
|
desktopInstallationFolders.forEach((folder) => {
|
|
@@ -6180,7 +6180,7 @@ var require_linux = __commonJS({
|
|
|
6180
6180
|
return;
|
|
6181
6181
|
}
|
|
6182
6182
|
try {
|
|
6183
|
-
const chromePath =
|
|
6183
|
+
const chromePath = execFileSync("which", [executable]).toString().split(newLineRegex)[0];
|
|
6184
6184
|
if (canAccess(chromePath)) {
|
|
6185
6185
|
installations.push(chromePath);
|
|
6186
6186
|
}
|
|
@@ -6750,8 +6750,8 @@ function getErrorMap() {
|
|
|
6750
6750
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
6751
6751
|
init_esm_shims();
|
|
6752
6752
|
var makeIssue = (params) => {
|
|
6753
|
-
const { data, path:
|
|
6754
|
-
const fullPath = [...
|
|
6753
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
6754
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
6755
6755
|
const fullIssue = {
|
|
6756
6756
|
...issueData,
|
|
6757
6757
|
path: fullPath
|
|
@@ -6871,11 +6871,11 @@ var errorUtil;
|
|
|
6871
6871
|
|
|
6872
6872
|
// node_modules/zod/v3/types.js
|
|
6873
6873
|
var ParseInputLazyPath = class {
|
|
6874
|
-
constructor(parent, value,
|
|
6874
|
+
constructor(parent, value, path16, key) {
|
|
6875
6875
|
this._cachedPath = [];
|
|
6876
6876
|
this.parent = parent;
|
|
6877
6877
|
this.data = value;
|
|
6878
|
-
this._path =
|
|
6878
|
+
this._path = path16;
|
|
6879
6879
|
this._key = key;
|
|
6880
6880
|
}
|
|
6881
6881
|
get path() {
|
|
@@ -10520,6 +10520,21 @@ var liveConfigSchema = external_exports.object({
|
|
|
10520
10520
|
// Debounce window for batched flushes (ms).
|
|
10521
10521
|
flushMs: external_exports.number().int().min(500).max(1e4).default(2e3)
|
|
10522
10522
|
}).default({});
|
|
10523
|
+
var redactConfigSchema = external_exports.object({
|
|
10524
|
+
enabled: external_exports.boolean().default(false),
|
|
10525
|
+
builtins: external_exports.boolean().default(true),
|
|
10526
|
+
patterns: external_exports.array(
|
|
10527
|
+
external_exports.string().refine((v) => {
|
|
10528
|
+
try {
|
|
10529
|
+
new RegExp(v);
|
|
10530
|
+
return true;
|
|
10531
|
+
} catch {
|
|
10532
|
+
return false;
|
|
10533
|
+
}
|
|
10534
|
+
}, "Each redact.patterns entry must be a valid regular expression")
|
|
10535
|
+
).default([]),
|
|
10536
|
+
mask: external_exports.string().default("[REDACTED]")
|
|
10537
|
+
}).default({});
|
|
10523
10538
|
var sectionFlagsShape = Object.fromEntries(
|
|
10524
10539
|
SECTION_KEYS.map((k) => [k, external_exports.boolean().optional()])
|
|
10525
10540
|
);
|
|
@@ -10540,7 +10555,7 @@ var ReporterOptionsSchema = external_exports.object({
|
|
|
10540
10555
|
primaryColor: hexColor.optional().default(DEFAULT_OPTIONS.primaryColor),
|
|
10541
10556
|
accentColor: hexColor.optional().default(DEFAULT_OPTIONS.accentColor),
|
|
10542
10557
|
watermark: external_exports.string().optional(),
|
|
10543
|
-
pdfPassword: external_exports.string().optional(),
|
|
10558
|
+
pdfPassword: external_exports.string().min(1, "pdfPassword must not be empty. Omit the option (or leave the env var unset) to disable encryption.").optional(),
|
|
10544
10559
|
reportTitle: external_exports.string().optional().default(DEFAULT_OPTIONS.reportTitle),
|
|
10545
10560
|
projectName: external_exports.string().optional(),
|
|
10546
10561
|
open: external_exports.boolean().optional().default(DEFAULT_OPTIONS.open),
|
|
@@ -10573,6 +10588,7 @@ var ReporterOptionsSchema = external_exports.object({
|
|
|
10573
10588
|
}, "requirementTagPattern must be a valid regular expression").optional().default(DEFAULT_OPTIONS.requirementTagPattern),
|
|
10574
10589
|
failureAnalysis: failureAnalysisConfigSchema,
|
|
10575
10590
|
capture: captureConfigSchema,
|
|
10591
|
+
redact: redactConfigSchema,
|
|
10576
10592
|
live: liveConfigSchema,
|
|
10577
10593
|
templatePath: external_exports.union([
|
|
10578
10594
|
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"),
|
|
@@ -12615,7 +12631,7 @@ var ShardMerger = class {
|
|
|
12615
12631
|
// src/pdf/PdfGenerator.ts
|
|
12616
12632
|
init_esm_shims();
|
|
12617
12633
|
import fs14 from "fs";
|
|
12618
|
-
import
|
|
12634
|
+
import path13 from "path";
|
|
12619
12635
|
|
|
12620
12636
|
// src/templates/engine.ts
|
|
12621
12637
|
init_esm_shims();
|
|
@@ -13150,49 +13166,38 @@ var ChartRenderer = class {
|
|
|
13150
13166
|
|
|
13151
13167
|
// src/pdf/PdfEncryptor.ts
|
|
13152
13168
|
init_esm_shims();
|
|
13153
|
-
import { execFileSync } from "child_process";
|
|
13154
13169
|
import fs11 from "fs";
|
|
13155
|
-
import
|
|
13156
|
-
|
|
13157
|
-
import crypto3 from "crypto";
|
|
13170
|
+
import { PDFDocument, PDFHeader } from "@cantoo/pdf-lib";
|
|
13171
|
+
var AES256_HEADER_MINOR = "7ext3";
|
|
13158
13172
|
var PdfEncryptor = class {
|
|
13159
13173
|
async encrypt(pdfPath, password) {
|
|
13160
13174
|
if (!password) return;
|
|
13161
|
-
if (!this.isQpdfAvailable()) {
|
|
13162
|
-
logger.warn(
|
|
13163
|
-
"pdfPassword is set but qpdf is not installed \u2014 PDF will NOT be encrypted. Install qpdf: https://qpdf.readthedocs.io/en/stable/installation.html"
|
|
13164
|
-
);
|
|
13165
|
-
return;
|
|
13166
|
-
}
|
|
13167
|
-
const tmpPath = path11.join(os10.tmpdir(), `rf-encrypted-${Date.now()}.pdf`);
|
|
13168
|
-
const argFile = path11.join(os10.tmpdir(), `rf-qpdf-args-${crypto3.randomBytes(8).toString("hex")}`);
|
|
13169
13175
|
try {
|
|
13170
|
-
const
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13176
|
+
const doc = await PDFDocument.load(await fs11.promises.readFile(pdfPath));
|
|
13177
|
+
doc.context.header = PDFHeader.forVersion(1, AES256_HEADER_MINOR);
|
|
13178
|
+
doc.encrypt({
|
|
13179
|
+
userPassword: password,
|
|
13180
|
+
ownerPassword: password,
|
|
13181
|
+
permissions: { printing: "highResolution", copying: false, modifying: false }
|
|
13182
|
+
});
|
|
13183
|
+
const encryptedBytes = await doc.save();
|
|
13184
|
+
const tmpPath = `${pdfPath}.rf-enc-tmp`;
|
|
13185
|
+
try {
|
|
13186
|
+
await fs11.promises.writeFile(tmpPath, encryptedBytes);
|
|
13187
|
+
await fs11.promises.rename(tmpPath, pdfPath);
|
|
13188
|
+
} finally {
|
|
13189
|
+
fs11.rmSync(tmpPath, { force: true });
|
|
13190
|
+
}
|
|
13191
|
+
logger.info("PDF encrypted with password (AES-256).");
|
|
13175
13192
|
} catch (e) {
|
|
13176
|
-
throw new Error(`
|
|
13177
|
-
} finally {
|
|
13178
|
-
if (fs11.existsSync(tmpPath)) fs11.unlinkSync(tmpPath);
|
|
13179
|
-
fs11.rmSync(argFile, { force: true });
|
|
13180
|
-
}
|
|
13181
|
-
}
|
|
13182
|
-
isQpdfAvailable() {
|
|
13183
|
-
try {
|
|
13184
|
-
execFileSync("qpdf", ["--version"], { stdio: "pipe" });
|
|
13185
|
-
return true;
|
|
13186
|
-
} catch (err) {
|
|
13187
|
-
logger.debug("qpdf probe failed \u2014 treating as not installed", { error: err.message });
|
|
13188
|
-
return false;
|
|
13193
|
+
throw new Error(`PDF encryption failed: ${e.message}`);
|
|
13189
13194
|
}
|
|
13190
13195
|
}
|
|
13191
13196
|
};
|
|
13192
13197
|
|
|
13193
13198
|
// src/filename/FilenameResolver.ts
|
|
13194
13199
|
init_esm_shims();
|
|
13195
|
-
import
|
|
13200
|
+
import path11 from "path";
|
|
13196
13201
|
import fs12 from "fs";
|
|
13197
13202
|
var MAX_BRANCH_LENGTH = 50;
|
|
13198
13203
|
var FilenameResolver = class {
|
|
@@ -13210,8 +13215,8 @@ var FilenameResolver = class {
|
|
|
13210
13215
|
const branch = this.sanitiseBranch(ctx.branch);
|
|
13211
13216
|
const status = ctx.status;
|
|
13212
13217
|
const resolved = template.replace(/\{date\}/g, date).replace(/\{branch\}/g, branch).replace(/\{status\}/g, status);
|
|
13213
|
-
const absolute =
|
|
13214
|
-
fs12.mkdirSync(
|
|
13218
|
+
const absolute = path11.isAbsolute(resolved) ? resolved : path11.resolve(process.cwd(), resolved);
|
|
13219
|
+
fs12.mkdirSync(path11.dirname(absolute), { recursive: true });
|
|
13215
13220
|
return absolute;
|
|
13216
13221
|
}
|
|
13217
13222
|
/**
|
|
@@ -13223,7 +13228,7 @@ var FilenameResolver = class {
|
|
|
13223
13228
|
* // → '/out/2026-report-executive.pdf'
|
|
13224
13229
|
*/
|
|
13225
13230
|
injectTemplateSuffix(outputPath, suffix) {
|
|
13226
|
-
const ext =
|
|
13231
|
+
const ext = path11.extname(outputPath);
|
|
13227
13232
|
const base = outputPath.slice(0, outputPath.length - ext.length);
|
|
13228
13233
|
return `${base}-${suffix}${ext}`;
|
|
13229
13234
|
}
|
|
@@ -13282,7 +13287,12 @@ var PRESETS = {
|
|
|
13282
13287
|
// src/pdf/FailurePaginator.ts
|
|
13283
13288
|
init_esm_shims();
|
|
13284
13289
|
import fs13 from "fs";
|
|
13285
|
-
import
|
|
13290
|
+
import path12 from "path";
|
|
13291
|
+
function sidecarPathFor(pdfPath) {
|
|
13292
|
+
const dir = path12.dirname(pdfPath);
|
|
13293
|
+
const base = path12.basename(pdfPath, path12.extname(pdfPath));
|
|
13294
|
+
return path12.join(dir, `${base}-failures.json`);
|
|
13295
|
+
}
|
|
13286
13296
|
var FailurePaginator = class {
|
|
13287
13297
|
async paginate(failures, maxInline, pdfOutputPath) {
|
|
13288
13298
|
if (failures.length <= maxInline) {
|
|
@@ -13290,7 +13300,7 @@ var FailurePaginator = class {
|
|
|
13290
13300
|
}
|
|
13291
13301
|
const inline = failures.slice(0, maxInline);
|
|
13292
13302
|
const overflow = failures.slice(maxInline);
|
|
13293
|
-
const sidecarPath =
|
|
13303
|
+
const sidecarPath = sidecarPathFor(pdfOutputPath);
|
|
13294
13304
|
try {
|
|
13295
13305
|
await fs13.promises.writeFile(
|
|
13296
13306
|
sidecarPath,
|
|
@@ -13298,7 +13308,7 @@ var FailurePaginator = class {
|
|
|
13298
13308
|
"utf8"
|
|
13299
13309
|
);
|
|
13300
13310
|
logger.info(
|
|
13301
|
-
`Emitted ${overflow.length} overflow failures \u2192 ${
|
|
13311
|
+
`Emitted ${overflow.length} overflow failures \u2192 ${path12.basename(sidecarPath)}`
|
|
13302
13312
|
);
|
|
13303
13313
|
} catch (e) {
|
|
13304
13314
|
logger.warn(`Could not write failure sidecar: ${e.message}`);
|
|
@@ -13306,11 +13316,6 @@ var FailurePaginator = class {
|
|
|
13306
13316
|
}
|
|
13307
13317
|
return { inline, overflowCount: overflow.length, sidecarPath };
|
|
13308
13318
|
}
|
|
13309
|
-
sidecarPathFor(pdfPath) {
|
|
13310
|
-
const dir = path13.dirname(pdfPath);
|
|
13311
|
-
const base = path13.basename(pdfPath, path13.extname(pdfPath));
|
|
13312
|
-
return path13.join(dir, `${base}-failures.json`);
|
|
13313
|
-
}
|
|
13314
13319
|
/**
|
|
13315
13320
|
* Drops the inlined image bytes — a 150-entry overflow would otherwise
|
|
13316
13321
|
* produce a 40 MB JSON. The path still references the on-disk screenshot
|
|
@@ -13325,6 +13330,21 @@ var FailurePaginator = class {
|
|
|
13325
13330
|
}
|
|
13326
13331
|
};
|
|
13327
13332
|
|
|
13333
|
+
// src/pdf/sidecar-crypto.ts
|
|
13334
|
+
init_esm_shims();
|
|
13335
|
+
import crypto3 from "crypto";
|
|
13336
|
+
var MAGIC = Buffer.from("RFENC1");
|
|
13337
|
+
var SALT_LEN = 16;
|
|
13338
|
+
var IV_LEN = 12;
|
|
13339
|
+
function encryptSidecar(plain, password) {
|
|
13340
|
+
const salt = crypto3.randomBytes(SALT_LEN);
|
|
13341
|
+
const key = crypto3.scryptSync(password, salt, 32, { N: 16384, r: 8, p: 1 });
|
|
13342
|
+
const iv = crypto3.randomBytes(IV_LEN);
|
|
13343
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", key, iv);
|
|
13344
|
+
const enc = Buffer.concat([cipher.update(plain), cipher.final()]);
|
|
13345
|
+
return Buffer.concat([MAGIC, salt, iv, cipher.getAuthTag(), enc]);
|
|
13346
|
+
}
|
|
13347
|
+
|
|
13328
13348
|
// src/pdf/PdfGenerator.ts
|
|
13329
13349
|
var PdfGenerator = class {
|
|
13330
13350
|
constructor() {
|
|
@@ -13422,7 +13442,7 @@ var PdfGenerator = class {
|
|
|
13422
13442
|
const templateId = source.kind === "builtin" ? source.id : "detailed";
|
|
13423
13443
|
const templateData = { ...data, meta: { ...data.meta, template: templateId } };
|
|
13424
13444
|
const templateOptions = { ...options, template: templateId };
|
|
13425
|
-
await this.renderPdf(templateData, templateOptions, source, compression, outputPath, page);
|
|
13445
|
+
let { sidecarPath } = await this.renderPdf(templateData, templateOptions, source, compression, outputPath, page);
|
|
13426
13446
|
const capBytes = (options.maxFileSizeMb ?? 0) * 1024 * 1024;
|
|
13427
13447
|
if (capBytes > 0) {
|
|
13428
13448
|
const sizeBytes = (await fs14.promises.stat(outputPath)).size;
|
|
@@ -13432,7 +13452,8 @@ var PdfGenerator = class {
|
|
|
13432
13452
|
logger.warn(
|
|
13433
13453
|
`PDF size ${fmtMb(sizeBytes)} exceeds cap ${fmtMb(capBytes)} \u2014 re-rendering (jpeg-q=${tighter.quality}, max-w=${tighter.maxWidth}px, inline-failures\u2264${tighter.maxInlineFailures}).`
|
|
13434
13454
|
);
|
|
13435
|
-
await this.renderPdf(templateData, templateOptions, source, tighter, outputPath, page);
|
|
13455
|
+
const retuned = await this.renderPdf(templateData, templateOptions, source, tighter, outputPath, page);
|
|
13456
|
+
sidecarPath = retuned.sidecarPath ?? sidecarPath;
|
|
13436
13457
|
const finalSize = (await fs14.promises.stat(outputPath)).size;
|
|
13437
13458
|
if (finalSize > capBytes) {
|
|
13438
13459
|
logger.warn(
|
|
@@ -13444,7 +13465,36 @@ var PdfGenerator = class {
|
|
|
13444
13465
|
}
|
|
13445
13466
|
}
|
|
13446
13467
|
if (options.pdfPassword) {
|
|
13447
|
-
|
|
13468
|
+
let pdfEncrypted = false;
|
|
13469
|
+
try {
|
|
13470
|
+
await this.pdfEncryptor.encrypt(outputPath, options.pdfPassword);
|
|
13471
|
+
pdfEncrypted = true;
|
|
13472
|
+
if (sidecarPath && fs14.existsSync(sidecarPath)) {
|
|
13473
|
+
const blob = encryptSidecar(await fs14.promises.readFile(sidecarPath), options.pdfPassword);
|
|
13474
|
+
await fs14.promises.writeFile(`${sidecarPath}.enc`, blob);
|
|
13475
|
+
await fs14.promises.unlink(sidecarPath);
|
|
13476
|
+
logger.info(`Overflow sidecar encrypted \u2192 ${path13.basename(sidecarPath)}.enc`);
|
|
13477
|
+
} else {
|
|
13478
|
+
const staleSidecarPath = sidecarPathFor(outputPath);
|
|
13479
|
+
if (fs14.existsSync(staleSidecarPath)) {
|
|
13480
|
+
fs14.rmSync(staleSidecarPath, { force: true });
|
|
13481
|
+
logger.info(`Removed stale plaintext sidecar: ${path13.basename(staleSidecarPath)}`);
|
|
13482
|
+
}
|
|
13483
|
+
}
|
|
13484
|
+
} catch (err) {
|
|
13485
|
+
const sidecarTargets = sidecarPath ? [sidecarPath, `${sidecarPath}.enc`] : [];
|
|
13486
|
+
const cleanupTargets = pdfEncrypted ? sidecarTargets : [outputPath, ...sidecarTargets];
|
|
13487
|
+
for (const target of cleanupTargets) {
|
|
13488
|
+
try {
|
|
13489
|
+
fs14.rmSync(target, { force: true });
|
|
13490
|
+
} catch (cleanupErr) {
|
|
13491
|
+
logger.warn(
|
|
13492
|
+
`Could not remove leftover file after encryption failure: ${target} (${cleanupErr.message})`
|
|
13493
|
+
);
|
|
13494
|
+
}
|
|
13495
|
+
}
|
|
13496
|
+
throw err;
|
|
13497
|
+
}
|
|
13448
13498
|
}
|
|
13449
13499
|
const finalSizeMb = fmtMb((await fs14.promises.stat(outputPath)).size);
|
|
13450
13500
|
logger.info(`PDF report generated: ${outputPath} (${finalSizeMb})`);
|
|
@@ -13459,17 +13509,17 @@ var PdfGenerator = class {
|
|
|
13459
13509
|
if (options.templatePath && options.templatePath.length > 0) {
|
|
13460
13510
|
const byAbsolutePath = /* @__PURE__ */ new Map();
|
|
13461
13511
|
for (const p of options.templatePath) {
|
|
13462
|
-
const absolutePath =
|
|
13512
|
+
const absolutePath = path13.isAbsolute(p) ? p : path13.resolve(process.cwd(), p);
|
|
13463
13513
|
if (!byAbsolutePath.has(absolutePath)) byAbsolutePath.set(absolutePath, p);
|
|
13464
13514
|
}
|
|
13465
13515
|
const labelCounts = /* @__PURE__ */ new Map();
|
|
13466
13516
|
for (const rawPath of byAbsolutePath.values()) {
|
|
13467
|
-
const label =
|
|
13517
|
+
const label = path13.basename(rawPath, ".hbs");
|
|
13468
13518
|
labelCounts.set(label, (labelCounts.get(label) ?? 0) + 1);
|
|
13469
13519
|
}
|
|
13470
13520
|
const seenPerLabel = /* @__PURE__ */ new Map();
|
|
13471
13521
|
return Array.from(byAbsolutePath.entries()).map(([absolutePath, rawPath]) => {
|
|
13472
|
-
const baseLabel =
|
|
13522
|
+
const baseLabel = path13.basename(rawPath, ".hbs");
|
|
13473
13523
|
let label = baseLabel;
|
|
13474
13524
|
if ((labelCounts.get(baseLabel) ?? 0) > 1) {
|
|
13475
13525
|
const n = (seenPerLabel.get(baseLabel) ?? 0) + 1;
|
|
@@ -13529,7 +13579,7 @@ var PdfGenerator = class {
|
|
|
13529
13579
|
...data,
|
|
13530
13580
|
failures: paginated.inline,
|
|
13531
13581
|
overflowFailureCount: paginated.overflowCount,
|
|
13532
|
-
overflowSidecarName: paginated.sidecarPath ?
|
|
13582
|
+
overflowSidecarName: paginated.sidecarPath ? path13.basename(paginated.sidecarPath) + (options.pdfPassword ? ".enc" : "") : void 0
|
|
13533
13583
|
};
|
|
13534
13584
|
for (const f of renderData.failures) f.screenshotBase64 = void 0;
|
|
13535
13585
|
if (options.includeScreenshots !== false) {
|
|
@@ -13554,7 +13604,7 @@ var PdfGenerator = class {
|
|
|
13554
13604
|
await page.goto(fileUrl, { waitUntil: "domcontentloaded", timeout: 6e4 });
|
|
13555
13605
|
const showCharts = source.kind === "builtin" ? resolveSections(source.id, options.sections).charts : true;
|
|
13556
13606
|
await this.chartRenderer.waitForCharts(page, showCharts);
|
|
13557
|
-
logger.info(`Generating PDF \u2192 ${
|
|
13607
|
+
logger.info(`Generating PDF \u2192 ${path13.relative(process.cwd(), outputPath)}`);
|
|
13558
13608
|
await page.pdf({
|
|
13559
13609
|
path: outputPath,
|
|
13560
13610
|
format: "A4",
|
|
@@ -13564,19 +13614,129 @@ var PdfGenerator = class {
|
|
|
13564
13614
|
} finally {
|
|
13565
13615
|
await this.htmlWriter.cleanup();
|
|
13566
13616
|
}
|
|
13617
|
+
return { sidecarPath: paginated.overflowCount > 0 ? paginated.sidecarPath : void 0 };
|
|
13567
13618
|
}
|
|
13568
13619
|
};
|
|
13569
13620
|
function fmtMb(bytes) {
|
|
13570
13621
|
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
13571
13622
|
}
|
|
13572
13623
|
|
|
13624
|
+
// src/redact/Redactor.ts
|
|
13625
|
+
init_esm_shims();
|
|
13626
|
+
var Redactor = class {
|
|
13627
|
+
constructor(opts) {
|
|
13628
|
+
/** Indexes of user patterns disabled after throwing (warned once each). */
|
|
13629
|
+
this.disabled = /* @__PURE__ */ new Set();
|
|
13630
|
+
this.maskRaw = opts.mask;
|
|
13631
|
+
this.maskReplacement = opts.mask.replace(/\$/g, "$$$$");
|
|
13632
|
+
this.useBuiltins = opts.builtins;
|
|
13633
|
+
this.userPatterns = opts.patterns.map((p) => new RegExp(p, "g"));
|
|
13634
|
+
}
|
|
13635
|
+
redactText(s) {
|
|
13636
|
+
if (!s) return s;
|
|
13637
|
+
let out = s;
|
|
13638
|
+
if (this.useBuiltins) out = this.applyBuiltins(out);
|
|
13639
|
+
for (let i = 0; i < this.userPatterns.length; i++) {
|
|
13640
|
+
if (this.disabled.has(i)) continue;
|
|
13641
|
+
try {
|
|
13642
|
+
out = out.replace(this.userPatterns[i], this.maskReplacement);
|
|
13643
|
+
} catch (err) {
|
|
13644
|
+
this.disabled.add(i);
|
|
13645
|
+
logger.warn(
|
|
13646
|
+
`redact: custom pattern #${i + 1} threw and is disabled for this run: ${err.message}`
|
|
13647
|
+
);
|
|
13648
|
+
}
|
|
13649
|
+
}
|
|
13650
|
+
return out;
|
|
13651
|
+
}
|
|
13652
|
+
applyBuiltins(s) {
|
|
13653
|
+
const M = this.maskRaw;
|
|
13654
|
+
const MR = this.maskReplacement;
|
|
13655
|
+
return s.replace(/(\b[a-z][\w+.-]{0,63}:\/\/[^/\s:@]{1,512}:)([^@\s/]{1,512})(?=@)/gi, `$1${MR}`).replace(
|
|
13656
|
+
/\b(?:(authorization\s*[:=]\s*basic\s+)([A-Za-z0-9._~+/=-]{8,})|((?:authorization\s*[:=]\s*)?bearer\s+)([A-Za-z0-9._~+/=-]{8,}))/gi,
|
|
13657
|
+
(fullMatch, basicPrefix, _basicToken, bearerPrefix, bearerToken) => {
|
|
13658
|
+
if (basicPrefix) return `${basicPrefix}${M}`;
|
|
13659
|
+
if (bearerToken && /[^a-z]/.test(bearerToken)) return `${bearerPrefix}${M}`;
|
|
13660
|
+
return fullMatch;
|
|
13661
|
+
}
|
|
13662
|
+
).replace(/\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\b/g, MR).replace(
|
|
13663
|
+
/\b(?:AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{16,}|gho_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|sk-[A-Za-z0-9_-]{16,}|rzp_(?:live|test)_[A-Za-z0-9]{8,}|AIza[A-Za-z0-9_-]{30,})\b/g,
|
|
13664
|
+
MR
|
|
13665
|
+
).replace(
|
|
13666
|
+
/\b((?:[A-Za-z0-9]{1,32}[_-]){0,8}(?:password|passwd|passcode|pwd|secret|token|api[_-]?key|apikey|auth|authorization|credential|private[_-]?key|access[_-]?key|client[_-]?secret))\b(["']?\s*[=:]\s*)(?!(?:bearer|basic)\b)(?:(["'])((?:(?!\3).){1,512})\3|([^\s"',;&]{1,512}))/gi,
|
|
13667
|
+
(_m, key, sep, q, _quotedVal, _plainVal) => q ? `${key}${sep}${q}${M}${q}` : `${key}${sep}${M}`
|
|
13668
|
+
).replace(
|
|
13669
|
+
/\b(fill|type|press)\b([^\n]{0,80}?\b(?:password|secret|token|pass|passcode|pin|otp)\b[^\n]{0,80}?\s)(["'])((?:(?!\3).)+)\3/gi,
|
|
13670
|
+
(_m, action, mid, q) => `${action}${mid}${q}${M}${q}`
|
|
13671
|
+
).replace(/\b([A-Za-z0-9._%+-])[A-Za-z0-9._%+-]{0,63}@([A-Za-z0-9.-]{1,255}\.[A-Za-z]{2,})\b/g, "$1***@$2").replace(/[A-Za-z0-9_+/=-]{20,}/g, (m, offset, str) => {
|
|
13672
|
+
const before = offset > 0 ? str[offset - 1] : " ";
|
|
13673
|
+
const after = offset + m.length < str.length ? str[offset + m.length] : " ";
|
|
13674
|
+
const beforeIsWord = /[A-Za-z0-9_]/.test(before);
|
|
13675
|
+
const afterIsWord = /[A-Za-z0-9_]/.test(after);
|
|
13676
|
+
if (!beforeIsWord && !afterIsWord && /\d/.test(m) && /[A-Za-z]/.test(m)) {
|
|
13677
|
+
return M;
|
|
13678
|
+
}
|
|
13679
|
+
return m;
|
|
13680
|
+
});
|
|
13681
|
+
}
|
|
13682
|
+
};
|
|
13683
|
+
|
|
13684
|
+
// src/redact/redact-report.ts
|
|
13685
|
+
init_esm_shims();
|
|
13686
|
+
function redactReportData(data, r) {
|
|
13687
|
+
const rd = (s) => s === void 0 ? void 0 : r.redactText(s);
|
|
13688
|
+
for (const f of data.failures) {
|
|
13689
|
+
f.testTitle = r.redactText(f.testTitle);
|
|
13690
|
+
f.suitePath = f.suitePath.map((s) => r.redactText(s));
|
|
13691
|
+
f.error.message = r.redactText(f.error.message);
|
|
13692
|
+
f.error.stack = rd(f.error.stack);
|
|
13693
|
+
if (f.execution) {
|
|
13694
|
+
f.execution.steps?.forEach((s) => redactStep(s, r));
|
|
13695
|
+
if (f.execution.failingStep) redactStep(f.execution.failingStep, r);
|
|
13696
|
+
if (f.execution.console) {
|
|
13697
|
+
f.execution.console.stdout = f.execution.console.stdout.map((l) => r.redactText(l));
|
|
13698
|
+
f.execution.console.stderr = f.execution.console.stderr.map((l) => r.redactText(l));
|
|
13699
|
+
}
|
|
13700
|
+
}
|
|
13701
|
+
}
|
|
13702
|
+
for (const p of data.projects) p.suites.forEach((s) => redactSuite(s, r));
|
|
13703
|
+
if (data.analysis) {
|
|
13704
|
+
for (const c of data.analysis.clusters) {
|
|
13705
|
+
c.representativeMessage = r.redactText(c.representativeMessage);
|
|
13706
|
+
c.tests = c.tests.map((t) => r.redactText(t));
|
|
13707
|
+
}
|
|
13708
|
+
data.analysis.oneLiner = r.redactText(data.analysis.oneLiner);
|
|
13709
|
+
}
|
|
13710
|
+
data.classifications?.forEach((c) => c.message = r.redactText(c.message));
|
|
13711
|
+
data.briefSentence = rd(data.briefSentence);
|
|
13712
|
+
data.charts.suiteResults.forEach((s) => s.label = r.redactText(s.label));
|
|
13713
|
+
data.charts.flakinessTable?.forEach((row) => row.title = r.redactText(row.title));
|
|
13714
|
+
data.environment.buildUrl = r.redactText(data.environment.buildUrl);
|
|
13715
|
+
}
|
|
13716
|
+
function redactStep(s, r) {
|
|
13717
|
+
s.title = r.redactText(s.title);
|
|
13718
|
+
if (s.errorMessage !== void 0) s.errorMessage = r.redactText(s.errorMessage);
|
|
13719
|
+
}
|
|
13720
|
+
function redactSuite(s, r) {
|
|
13721
|
+
s.title = r.redactText(s.title);
|
|
13722
|
+
for (const t of s.tests) {
|
|
13723
|
+
t.title = r.redactText(t.title);
|
|
13724
|
+
t.fullTitle = r.redactText(t.fullTitle);
|
|
13725
|
+
t.tags = t.tags.map((tag) => r.redactText(tag));
|
|
13726
|
+
t.annotations.forEach((a) => {
|
|
13727
|
+
if (a.description !== void 0) a.description = r.redactText(a.description);
|
|
13728
|
+
});
|
|
13729
|
+
}
|
|
13730
|
+
s.suites.forEach((child) => redactSuite(child, r));
|
|
13731
|
+
}
|
|
13732
|
+
|
|
13573
13733
|
// src/reporter.ts
|
|
13574
|
-
import
|
|
13734
|
+
import path15 from "path";
|
|
13575
13735
|
|
|
13576
13736
|
// src/history/HistoryManager.ts
|
|
13577
13737
|
init_esm_shims();
|
|
13578
13738
|
import fs15 from "fs";
|
|
13579
|
-
import
|
|
13739
|
+
import path14 from "path";
|
|
13580
13740
|
var HistoryManager = class {
|
|
13581
13741
|
constructor(filePath) {
|
|
13582
13742
|
this.filePath = filePath;
|
|
@@ -13597,7 +13757,7 @@ var HistoryManager = class {
|
|
|
13597
13757
|
}
|
|
13598
13758
|
append(entry, maxSize) {
|
|
13599
13759
|
try {
|
|
13600
|
-
fs15.mkdirSync(
|
|
13760
|
+
fs15.mkdirSync(path14.dirname(this.filePath), { recursive: true });
|
|
13601
13761
|
const next = {
|
|
13602
13762
|
version: 1,
|
|
13603
13763
|
entries: [entry, ...this.read()].slice(0, maxSize)
|
|
@@ -14100,6 +14260,10 @@ var LiveStreamer = class {
|
|
|
14100
14260
|
}
|
|
14101
14261
|
enqueueTest(update) {
|
|
14102
14262
|
if (!this.enabled) return;
|
|
14263
|
+
if (this.config.redact) {
|
|
14264
|
+
if (update.title !== void 0) update.title = this.config.redact(update.title);
|
|
14265
|
+
if (update.currentStep !== void 0) update.currentStep = this.config.redact(update.currentStep);
|
|
14266
|
+
}
|
|
14103
14267
|
const prevRetry = this.testStates.get(update.id)?.retry;
|
|
14104
14268
|
this.testStates.set(update.id, { status: update.status, retry: update.retry ?? prevRetry });
|
|
14105
14269
|
this.testBuffer.push(update);
|
|
@@ -14108,6 +14272,10 @@ var LiveStreamer = class {
|
|
|
14108
14272
|
}
|
|
14109
14273
|
enqueueEvent(event) {
|
|
14110
14274
|
if (!this.enabled) return;
|
|
14275
|
+
if (this.config.redact) {
|
|
14276
|
+
if (event.title !== void 0) event.title = this.config.redact(event.title);
|
|
14277
|
+
if (event.text !== void 0) event.text = this.config.redact(event.text);
|
|
14278
|
+
}
|
|
14111
14279
|
event.id = `${this.config.shardKey}:${this.eventSeq++}`;
|
|
14112
14280
|
this.eventBuffer.push(event);
|
|
14113
14281
|
this.capPreLicenseBuffer();
|
|
@@ -14414,7 +14582,10 @@ var PdfReporter = class {
|
|
|
14414
14582
|
this.options = parseOptions(rawOptions);
|
|
14415
14583
|
setLogLevel(this.options.logLevel);
|
|
14416
14584
|
this.dataCollector = new DataCollector(this.options.capture);
|
|
14417
|
-
this.
|
|
14585
|
+
if (this.options.redact.enabled) {
|
|
14586
|
+
this.redactor = new Redactor(this.options.redact);
|
|
14587
|
+
}
|
|
14588
|
+
this.version = true ? "0.23.0" : "0.x";
|
|
14418
14589
|
logger.info(`@reportforge/playwright-pdf v${this.version} initialised`);
|
|
14419
14590
|
this.licenseClient = new LicenseClient({
|
|
14420
14591
|
licenseKey: this.options.licenseKey,
|
|
@@ -14505,6 +14676,9 @@ var PdfReporter = class {
|
|
|
14505
14676
|
const reportData = this._buildReportData(collected, licenseInfo);
|
|
14506
14677
|
runFailureAnalysis(reportData, this.options, this.cwd);
|
|
14507
14678
|
reportData.briefSentence = buildBriefSentence(reportData.stats, reportData.analysis, reportData.charts.trend);
|
|
14679
|
+
if (this.redactor) {
|
|
14680
|
+
redactReportData(reportData, this.redactor);
|
|
14681
|
+
}
|
|
14508
14682
|
let pdfPaths = [];
|
|
14509
14683
|
try {
|
|
14510
14684
|
pdfPaths = await this.pdfGenerator.generateAll(reportData, this.options, licenseInfo);
|
|
@@ -14536,7 +14710,10 @@ var PdfReporter = class {
|
|
|
14536
14710
|
const historyPath = resolveHistoryPath(this.options, this.cwd);
|
|
14537
14711
|
const hm = new HistoryManager(historyPath);
|
|
14538
14712
|
const suffix = Math.random().toString(36).padEnd(8, "0").slice(2, 6);
|
|
14539
|
-
const
|
|
14713
|
+
const redactor = this.redactor;
|
|
14714
|
+
const flakyTests = collectFlakyTests(collected.projects).map(
|
|
14715
|
+
(t) => redactor ? { ...t, title: redactor.redactText(t.title) } : t
|
|
14716
|
+
);
|
|
14540
14717
|
const entry = {
|
|
14541
14718
|
runId: `${(/* @__PURE__ */ new Date()).toISOString()}-${suffix}`,
|
|
14542
14719
|
timestamp: Date.now(),
|
|
@@ -14676,6 +14853,7 @@ var PdfReporter = class {
|
|
|
14676
14853
|
const token = computeWatchToken(runId, this.options.licenseKey);
|
|
14677
14854
|
const watchUrl = token ? buildWatchUrl(serverUrl, runId, token) : void 0;
|
|
14678
14855
|
const env = detectCiEnv();
|
|
14856
|
+
if (this.redactor && env?.buildUrl) env.buildUrl = this.redactor.redactText(env.buildUrl);
|
|
14679
14857
|
if (watchUrl) {
|
|
14680
14858
|
void this.announceLive(watchUrl, env, shard).catch(() => {
|
|
14681
14859
|
});
|
|
@@ -14690,6 +14868,7 @@ var PdfReporter = class {
|
|
|
14690
14868
|
shardKey: this.liveShardKey,
|
|
14691
14869
|
shardTotal: shard?.total ?? null,
|
|
14692
14870
|
license: () => this.license(),
|
|
14871
|
+
redact: this.redactor ? (s) => this.redactor.redactText(s) : void 0,
|
|
14693
14872
|
env,
|
|
14694
14873
|
flushMs: live.flushMs
|
|
14695
14874
|
});
|
|
@@ -14731,7 +14910,7 @@ var PdfReporter = class {
|
|
|
14731
14910
|
}
|
|
14732
14911
|
resolveProjectName() {
|
|
14733
14912
|
try {
|
|
14734
|
-
const pkgPath =
|
|
14913
|
+
const pkgPath = path15.resolve(process.cwd(), "package.json");
|
|
14735
14914
|
const pkg = __require(pkgPath);
|
|
14736
14915
|
return pkg.name ?? "Playwright Tests";
|
|
14737
14916
|
} catch {
|
|
@@ -14781,9 +14960,9 @@ function deriveVerdict(stats) {
|
|
|
14781
14960
|
}
|
|
14782
14961
|
function resolveHistoryPath(options, cwd) {
|
|
14783
14962
|
if (options.historyFile) {
|
|
14784
|
-
return
|
|
14963
|
+
return path15.isAbsolute(options.historyFile) ? options.historyFile : path15.resolve(cwd, options.historyFile);
|
|
14785
14964
|
}
|
|
14786
|
-
return
|
|
14965
|
+
return path15.join(resolveProjectDir(options, cwd), "history.json");
|
|
14787
14966
|
}
|
|
14788
14967
|
function flattenSuiteNodes(suites) {
|
|
14789
14968
|
return suites.flatMap((s) => [s, ...flattenSuiteNodes(s.suites)]);
|