@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.js
CHANGED
|
@@ -1221,13 +1221,13 @@ var require_ast = __commonJS({
|
|
|
1221
1221
|
helperExpression: function helperExpression(node) {
|
|
1222
1222
|
return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
|
|
1223
1223
|
},
|
|
1224
|
-
scopedId: function scopedId(
|
|
1225
|
-
return /^\.|this\b/.test(
|
|
1224
|
+
scopedId: function scopedId(path15) {
|
|
1225
|
+
return /^\.|this\b/.test(path15.original);
|
|
1226
1226
|
},
|
|
1227
1227
|
// an ID is simple if it only has one part, and that part is not
|
|
1228
1228
|
// `..` or `this`.
|
|
1229
|
-
simpleId: function simpleId(
|
|
1230
|
-
return
|
|
1229
|
+
simpleId: function simpleId(path15) {
|
|
1230
|
+
return path15.parts.length === 1 && !AST.helpers.scopedId(path15) && !path15.depth;
|
|
1231
1231
|
}
|
|
1232
1232
|
}
|
|
1233
1233
|
};
|
|
@@ -2301,12 +2301,12 @@ var require_helpers2 = __commonJS({
|
|
|
2301
2301
|
loc
|
|
2302
2302
|
};
|
|
2303
2303
|
}
|
|
2304
|
-
function prepareMustache(
|
|
2304
|
+
function prepareMustache(path15, params, hash, open, strip, locInfo) {
|
|
2305
2305
|
var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
|
|
2306
2306
|
var decorator = /\*/.test(open);
|
|
2307
2307
|
return {
|
|
2308
2308
|
type: decorator ? "Decorator" : "MustacheStatement",
|
|
2309
|
-
path:
|
|
2309
|
+
path: path15,
|
|
2310
2310
|
params,
|
|
2311
2311
|
hash,
|
|
2312
2312
|
escaped,
|
|
@@ -2626,9 +2626,9 @@ var require_compiler = __commonJS({
|
|
|
2626
2626
|
},
|
|
2627
2627
|
DecoratorBlock: function DecoratorBlock(decorator) {
|
|
2628
2628
|
var program = decorator.program && this.compileProgram(decorator.program);
|
|
2629
|
-
var params = this.setupFullMustacheParams(decorator, program, void 0),
|
|
2629
|
+
var params = this.setupFullMustacheParams(decorator, program, void 0), path15 = decorator.path;
|
|
2630
2630
|
this.useDecorators = true;
|
|
2631
|
-
this.opcode("registerDecorator", params.length,
|
|
2631
|
+
this.opcode("registerDecorator", params.length, path15.original);
|
|
2632
2632
|
},
|
|
2633
2633
|
PartialStatement: function PartialStatement(partial) {
|
|
2634
2634
|
this.usePartial = true;
|
|
@@ -2692,46 +2692,46 @@ var require_compiler = __commonJS({
|
|
|
2692
2692
|
}
|
|
2693
2693
|
},
|
|
2694
2694
|
ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
|
|
2695
|
-
var
|
|
2696
|
-
this.opcode("getContext",
|
|
2695
|
+
var path15 = sexpr.path, name = path15.parts[0], isBlock = program != null || inverse != null;
|
|
2696
|
+
this.opcode("getContext", path15.depth);
|
|
2697
2697
|
this.opcode("pushProgram", program);
|
|
2698
2698
|
this.opcode("pushProgram", inverse);
|
|
2699
|
-
|
|
2700
|
-
this.accept(
|
|
2699
|
+
path15.strict = true;
|
|
2700
|
+
this.accept(path15);
|
|
2701
2701
|
this.opcode("invokeAmbiguous", name, isBlock);
|
|
2702
2702
|
},
|
|
2703
2703
|
simpleSexpr: function simpleSexpr(sexpr) {
|
|
2704
|
-
var
|
|
2705
|
-
|
|
2706
|
-
this.accept(
|
|
2704
|
+
var path15 = sexpr.path;
|
|
2705
|
+
path15.strict = true;
|
|
2706
|
+
this.accept(path15);
|
|
2707
2707
|
this.opcode("resolvePossibleLambda");
|
|
2708
2708
|
},
|
|
2709
2709
|
helperSexpr: function helperSexpr(sexpr, program, inverse) {
|
|
2710
|
-
var params = this.setupFullMustacheParams(sexpr, program, inverse),
|
|
2710
|
+
var params = this.setupFullMustacheParams(sexpr, program, inverse), path15 = sexpr.path, name = path15.parts[0];
|
|
2711
2711
|
if (this.options.knownHelpers[name]) {
|
|
2712
2712
|
this.opcode("invokeKnownHelper", params.length, name);
|
|
2713
2713
|
} else if (this.options.knownHelpersOnly) {
|
|
2714
2714
|
throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
|
|
2715
2715
|
} else {
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
this.accept(
|
|
2719
|
-
this.opcode("invokeHelper", params.length,
|
|
2716
|
+
path15.strict = true;
|
|
2717
|
+
path15.falsy = true;
|
|
2718
|
+
this.accept(path15);
|
|
2719
|
+
this.opcode("invokeHelper", params.length, path15.original, _ast2["default"].helpers.simpleId(path15));
|
|
2720
2720
|
}
|
|
2721
2721
|
},
|
|
2722
|
-
PathExpression: function PathExpression(
|
|
2723
|
-
this.addDepth(
|
|
2724
|
-
this.opcode("getContext",
|
|
2725
|
-
var name =
|
|
2722
|
+
PathExpression: function PathExpression(path15) {
|
|
2723
|
+
this.addDepth(path15.depth);
|
|
2724
|
+
this.opcode("getContext", path15.depth);
|
|
2725
|
+
var name = path15.parts[0], scoped = _ast2["default"].helpers.scopedId(path15), blockParamId = !path15.depth && !scoped && this.blockParamIndex(name);
|
|
2726
2726
|
if (blockParamId) {
|
|
2727
|
-
this.opcode("lookupBlockParam", blockParamId,
|
|
2727
|
+
this.opcode("lookupBlockParam", blockParamId, path15.parts);
|
|
2728
2728
|
} else if (!name) {
|
|
2729
2729
|
this.opcode("pushContext");
|
|
2730
|
-
} else if (
|
|
2730
|
+
} else if (path15.data) {
|
|
2731
2731
|
this.options.data = true;
|
|
2732
|
-
this.opcode("lookupData",
|
|
2732
|
+
this.opcode("lookupData", path15.depth, path15.parts, path15.strict);
|
|
2733
2733
|
} else {
|
|
2734
|
-
this.opcode("lookupOnContext",
|
|
2734
|
+
this.opcode("lookupOnContext", path15.parts, path15.falsy, path15.strict, scoped);
|
|
2735
2735
|
}
|
|
2736
2736
|
},
|
|
2737
2737
|
StringLiteral: function StringLiteral(string) {
|
|
@@ -3087,16 +3087,16 @@ var require_util = __commonJS({
|
|
|
3087
3087
|
}
|
|
3088
3088
|
exports2.urlGenerate = urlGenerate;
|
|
3089
3089
|
function normalize(aPath) {
|
|
3090
|
-
var
|
|
3090
|
+
var path15 = aPath;
|
|
3091
3091
|
var url = urlParse(aPath);
|
|
3092
3092
|
if (url) {
|
|
3093
3093
|
if (!url.path) {
|
|
3094
3094
|
return aPath;
|
|
3095
3095
|
}
|
|
3096
|
-
|
|
3096
|
+
path15 = url.path;
|
|
3097
3097
|
}
|
|
3098
|
-
var isAbsolute = exports2.isAbsolute(
|
|
3099
|
-
var parts =
|
|
3098
|
+
var isAbsolute = exports2.isAbsolute(path15);
|
|
3099
|
+
var parts = path15.split(/\/+/);
|
|
3100
3100
|
for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
|
|
3101
3101
|
part = parts[i];
|
|
3102
3102
|
if (part === ".") {
|
|
@@ -3113,15 +3113,15 @@ var require_util = __commonJS({
|
|
|
3113
3113
|
}
|
|
3114
3114
|
}
|
|
3115
3115
|
}
|
|
3116
|
-
|
|
3117
|
-
if (
|
|
3118
|
-
|
|
3116
|
+
path15 = parts.join("/");
|
|
3117
|
+
if (path15 === "") {
|
|
3118
|
+
path15 = isAbsolute ? "/" : ".";
|
|
3119
3119
|
}
|
|
3120
3120
|
if (url) {
|
|
3121
|
-
url.path =
|
|
3121
|
+
url.path = path15;
|
|
3122
3122
|
return urlGenerate(url);
|
|
3123
3123
|
}
|
|
3124
|
-
return
|
|
3124
|
+
return path15;
|
|
3125
3125
|
}
|
|
3126
3126
|
exports2.normalize = normalize;
|
|
3127
3127
|
function join(aRoot, aPath) {
|
|
@@ -5924,8 +5924,8 @@ var require_printer = __commonJS({
|
|
|
5924
5924
|
return this.accept(sexpr.path) + " " + params + hash;
|
|
5925
5925
|
};
|
|
5926
5926
|
PrintVisitor.prototype.PathExpression = function(id) {
|
|
5927
|
-
var
|
|
5928
|
-
return (id.data ? "@" : "") + "PATH:" +
|
|
5927
|
+
var path15 = id.parts.join("/");
|
|
5928
|
+
return (id.data ? "@" : "") + "PATH:" + path15;
|
|
5929
5929
|
};
|
|
5930
5930
|
PrintVisitor.prototype.StringLiteral = function(string) {
|
|
5931
5931
|
return '"' + string.value + '"';
|
|
@@ -6045,7 +6045,7 @@ var require_win32 = __commonJS({
|
|
|
6045
6045
|
"node_modules/chrome-finder/lib/win32.js"(exports2, module2) {
|
|
6046
6046
|
"use strict";
|
|
6047
6047
|
init_cjs_shims();
|
|
6048
|
-
var
|
|
6048
|
+
var path15 = require("path");
|
|
6049
6049
|
var { canAccess } = require_util2();
|
|
6050
6050
|
function win32() {
|
|
6051
6051
|
const installations = [];
|
|
@@ -6059,7 +6059,7 @@ var require_win32 = __commonJS({
|
|
|
6059
6059
|
const prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"]];
|
|
6060
6060
|
prefixes.forEach((prefix) => suffixes.forEach((suffix) => {
|
|
6061
6061
|
if (prefix) {
|
|
6062
|
-
const chromePath =
|
|
6062
|
+
const chromePath = path15.join(prefix, suffix);
|
|
6063
6063
|
if (canAccess(chromePath)) {
|
|
6064
6064
|
installations.push(chromePath);
|
|
6065
6065
|
}
|
|
@@ -6077,7 +6077,7 @@ var require_darwin = __commonJS({
|
|
|
6077
6077
|
"use strict";
|
|
6078
6078
|
init_cjs_shims();
|
|
6079
6079
|
var { execSync: execSync3 } = require("child_process");
|
|
6080
|
-
var
|
|
6080
|
+
var path15 = require("path");
|
|
6081
6081
|
var { canAccess, newLineRegex, sort } = require_util2();
|
|
6082
6082
|
function darwin() {
|
|
6083
6083
|
const suffixes = ["/Contents/MacOS/Google Chrome Canary", "/Contents/MacOS/Google Chrome", "/Contents/MacOS/Chromium"];
|
|
@@ -6087,7 +6087,7 @@ var require_darwin = __commonJS({
|
|
|
6087
6087
|
${LSREGISTER} -dump | grep -E -i -o '/.+(google chrome( canary)?|chromium)\\.app(\\s|$)' | grep -E -v 'Caches|TimeMachine|Temporary|/Volumes|\\.Trash'
|
|
6088
6088
|
`).toString().split(newLineRegex).forEach((inst) => {
|
|
6089
6089
|
suffixes.forEach((suffix) => {
|
|
6090
|
-
const execPath =
|
|
6090
|
+
const execPath = path15.join(inst.trim(), suffix);
|
|
6091
6091
|
if (canAccess(execPath)) {
|
|
6092
6092
|
installations.push(execPath);
|
|
6093
6093
|
}
|
|
@@ -6115,8 +6115,8 @@ var require_linux = __commonJS({
|
|
|
6115
6115
|
"node_modules/chrome-finder/lib/linux.js"(exports2, module2) {
|
|
6116
6116
|
"use strict";
|
|
6117
6117
|
init_cjs_shims();
|
|
6118
|
-
var { execSync: execSync3, execFileSync
|
|
6119
|
-
var
|
|
6118
|
+
var { execSync: execSync3, execFileSync } = require("child_process");
|
|
6119
|
+
var path15 = require("path");
|
|
6120
6120
|
var fs16 = require("fs");
|
|
6121
6121
|
var { canAccess, sort, isExecutable, newLineRegex } = require_util2();
|
|
6122
6122
|
function findChromeExecutablesForLinuxDesktop(folder) {
|
|
@@ -6134,7 +6134,7 @@ var require_linux = __commonJS({
|
|
|
6134
6134
|
function linux() {
|
|
6135
6135
|
let installations = [];
|
|
6136
6136
|
const desktopInstallationFolders = [
|
|
6137
|
-
|
|
6137
|
+
path15.join(require("os").homedir(), ".local/share/applications/"),
|
|
6138
6138
|
"/usr/share/applications/"
|
|
6139
6139
|
];
|
|
6140
6140
|
desktopInstallationFolders.forEach((folder) => {
|
|
@@ -6172,7 +6172,7 @@ var require_linux = __commonJS({
|
|
|
6172
6172
|
return;
|
|
6173
6173
|
}
|
|
6174
6174
|
try {
|
|
6175
|
-
const chromePath =
|
|
6175
|
+
const chromePath = execFileSync("which", [executable]).toString().split(newLineRegex)[0];
|
|
6176
6176
|
if (canAccess(chromePath)) {
|
|
6177
6177
|
installations.push(chromePath);
|
|
6178
6178
|
}
|
|
@@ -6749,8 +6749,8 @@ function getErrorMap() {
|
|
|
6749
6749
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
6750
6750
|
init_cjs_shims();
|
|
6751
6751
|
var makeIssue = (params) => {
|
|
6752
|
-
const { data, path:
|
|
6753
|
-
const fullPath = [...
|
|
6752
|
+
const { data, path: path15, errorMaps, issueData } = params;
|
|
6753
|
+
const fullPath = [...path15, ...issueData.path || []];
|
|
6754
6754
|
const fullIssue = {
|
|
6755
6755
|
...issueData,
|
|
6756
6756
|
path: fullPath
|
|
@@ -6870,11 +6870,11 @@ var errorUtil;
|
|
|
6870
6870
|
|
|
6871
6871
|
// node_modules/zod/v3/types.js
|
|
6872
6872
|
var ParseInputLazyPath = class {
|
|
6873
|
-
constructor(parent, value,
|
|
6873
|
+
constructor(parent, value, path15, key) {
|
|
6874
6874
|
this._cachedPath = [];
|
|
6875
6875
|
this.parent = parent;
|
|
6876
6876
|
this.data = value;
|
|
6877
|
-
this._path =
|
|
6877
|
+
this._path = path15;
|
|
6878
6878
|
this._key = key;
|
|
6879
6879
|
}
|
|
6880
6880
|
get path() {
|
|
@@ -10519,6 +10519,21 @@ var liveConfigSchema = external_exports.object({
|
|
|
10519
10519
|
// Debounce window for batched flushes (ms).
|
|
10520
10520
|
flushMs: external_exports.number().int().min(500).max(1e4).default(2e3)
|
|
10521
10521
|
}).default({});
|
|
10522
|
+
var redactConfigSchema = external_exports.object({
|
|
10523
|
+
enabled: external_exports.boolean().default(false),
|
|
10524
|
+
builtins: external_exports.boolean().default(true),
|
|
10525
|
+
patterns: external_exports.array(
|
|
10526
|
+
external_exports.string().refine((v) => {
|
|
10527
|
+
try {
|
|
10528
|
+
new RegExp(v);
|
|
10529
|
+
return true;
|
|
10530
|
+
} catch {
|
|
10531
|
+
return false;
|
|
10532
|
+
}
|
|
10533
|
+
}, "Each redact.patterns entry must be a valid regular expression")
|
|
10534
|
+
).default([]),
|
|
10535
|
+
mask: external_exports.string().default("[REDACTED]")
|
|
10536
|
+
}).default({});
|
|
10522
10537
|
var sectionFlagsShape = Object.fromEntries(
|
|
10523
10538
|
SECTION_KEYS.map((k) => [k, external_exports.boolean().optional()])
|
|
10524
10539
|
);
|
|
@@ -10539,7 +10554,7 @@ var ReporterOptionsSchema = external_exports.object({
|
|
|
10539
10554
|
primaryColor: hexColor.optional().default(DEFAULT_OPTIONS.primaryColor),
|
|
10540
10555
|
accentColor: hexColor.optional().default(DEFAULT_OPTIONS.accentColor),
|
|
10541
10556
|
watermark: external_exports.string().optional(),
|
|
10542
|
-
pdfPassword: external_exports.string().optional(),
|
|
10557
|
+
pdfPassword: external_exports.string().min(1, "pdfPassword must not be empty. Omit the option (or leave the env var unset) to disable encryption.").optional(),
|
|
10543
10558
|
reportTitle: external_exports.string().optional().default(DEFAULT_OPTIONS.reportTitle),
|
|
10544
10559
|
projectName: external_exports.string().optional(),
|
|
10545
10560
|
open: external_exports.boolean().optional().default(DEFAULT_OPTIONS.open),
|
|
@@ -10572,6 +10587,7 @@ var ReporterOptionsSchema = external_exports.object({
|
|
|
10572
10587
|
}, "requirementTagPattern must be a valid regular expression").optional().default(DEFAULT_OPTIONS.requirementTagPattern),
|
|
10573
10588
|
failureAnalysis: failureAnalysisConfigSchema,
|
|
10574
10589
|
capture: captureConfigSchema,
|
|
10590
|
+
redact: redactConfigSchema,
|
|
10575
10591
|
live: liveConfigSchema,
|
|
10576
10592
|
templatePath: external_exports.union([
|
|
10577
10593
|
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"),
|
|
@@ -12614,7 +12630,7 @@ var ShardMerger = class {
|
|
|
12614
12630
|
// src/pdf/PdfGenerator.ts
|
|
12615
12631
|
init_cjs_shims();
|
|
12616
12632
|
var import_fs11 = __toESM(require("fs"));
|
|
12617
|
-
var
|
|
12633
|
+
var import_path7 = __toESM(require("path"));
|
|
12618
12634
|
|
|
12619
12635
|
// src/templates/engine.ts
|
|
12620
12636
|
init_cjs_shims();
|
|
@@ -13149,49 +13165,38 @@ var ChartRenderer = class {
|
|
|
13149
13165
|
|
|
13150
13166
|
// src/pdf/PdfEncryptor.ts
|
|
13151
13167
|
init_cjs_shims();
|
|
13152
|
-
var import_child_process3 = require("child_process");
|
|
13153
13168
|
var import_fs8 = __toESM(require("fs"));
|
|
13154
|
-
var
|
|
13155
|
-
var
|
|
13156
|
-
var import_crypto7 = __toESM(require("crypto"));
|
|
13169
|
+
var import_pdf_lib = require("@cantoo/pdf-lib");
|
|
13170
|
+
var AES256_HEADER_MINOR = "7ext3";
|
|
13157
13171
|
var PdfEncryptor = class {
|
|
13158
13172
|
async encrypt(pdfPath, password) {
|
|
13159
13173
|
if (!password) return;
|
|
13160
|
-
if (!this.isQpdfAvailable()) {
|
|
13161
|
-
logger.warn(
|
|
13162
|
-
"pdfPassword is set but qpdf is not installed \u2014 PDF will NOT be encrypted. Install qpdf: https://qpdf.readthedocs.io/en/stable/installation.html"
|
|
13163
|
-
);
|
|
13164
|
-
return;
|
|
13165
|
-
}
|
|
13166
|
-
const tmpPath = import_path5.default.join(import_os5.default.tmpdir(), `rf-encrypted-${Date.now()}.pdf`);
|
|
13167
|
-
const argFile = import_path5.default.join(import_os5.default.tmpdir(), `rf-qpdf-args-${import_crypto7.default.randomBytes(8).toString("hex")}`);
|
|
13168
13174
|
try {
|
|
13169
|
-
const
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13175
|
+
const doc = await import_pdf_lib.PDFDocument.load(await import_fs8.default.promises.readFile(pdfPath));
|
|
13176
|
+
doc.context.header = import_pdf_lib.PDFHeader.forVersion(1, AES256_HEADER_MINOR);
|
|
13177
|
+
doc.encrypt({
|
|
13178
|
+
userPassword: password,
|
|
13179
|
+
ownerPassword: password,
|
|
13180
|
+
permissions: { printing: "highResolution", copying: false, modifying: false }
|
|
13181
|
+
});
|
|
13182
|
+
const encryptedBytes = await doc.save();
|
|
13183
|
+
const tmpPath = `${pdfPath}.rf-enc-tmp`;
|
|
13184
|
+
try {
|
|
13185
|
+
await import_fs8.default.promises.writeFile(tmpPath, encryptedBytes);
|
|
13186
|
+
await import_fs8.default.promises.rename(tmpPath, pdfPath);
|
|
13187
|
+
} finally {
|
|
13188
|
+
import_fs8.default.rmSync(tmpPath, { force: true });
|
|
13189
|
+
}
|
|
13190
|
+
logger.info("PDF encrypted with password (AES-256).");
|
|
13174
13191
|
} catch (e) {
|
|
13175
|
-
throw new Error(`
|
|
13176
|
-
} finally {
|
|
13177
|
-
if (import_fs8.default.existsSync(tmpPath)) import_fs8.default.unlinkSync(tmpPath);
|
|
13178
|
-
import_fs8.default.rmSync(argFile, { force: true });
|
|
13179
|
-
}
|
|
13180
|
-
}
|
|
13181
|
-
isQpdfAvailable() {
|
|
13182
|
-
try {
|
|
13183
|
-
(0, import_child_process3.execFileSync)("qpdf", ["--version"], { stdio: "pipe" });
|
|
13184
|
-
return true;
|
|
13185
|
-
} catch (err) {
|
|
13186
|
-
logger.debug("qpdf probe failed \u2014 treating as not installed", { error: err.message });
|
|
13187
|
-
return false;
|
|
13192
|
+
throw new Error(`PDF encryption failed: ${e.message}`);
|
|
13188
13193
|
}
|
|
13189
13194
|
}
|
|
13190
13195
|
};
|
|
13191
13196
|
|
|
13192
13197
|
// src/filename/FilenameResolver.ts
|
|
13193
13198
|
init_cjs_shims();
|
|
13194
|
-
var
|
|
13199
|
+
var import_path5 = __toESM(require("path"));
|
|
13195
13200
|
var import_fs9 = __toESM(require("fs"));
|
|
13196
13201
|
var MAX_BRANCH_LENGTH = 50;
|
|
13197
13202
|
var FilenameResolver = class {
|
|
@@ -13209,8 +13214,8 @@ var FilenameResolver = class {
|
|
|
13209
13214
|
const branch = this.sanitiseBranch(ctx.branch);
|
|
13210
13215
|
const status = ctx.status;
|
|
13211
13216
|
const resolved = template.replace(/\{date\}/g, date).replace(/\{branch\}/g, branch).replace(/\{status\}/g, status);
|
|
13212
|
-
const absolute =
|
|
13213
|
-
import_fs9.default.mkdirSync(
|
|
13217
|
+
const absolute = import_path5.default.isAbsolute(resolved) ? resolved : import_path5.default.resolve(process.cwd(), resolved);
|
|
13218
|
+
import_fs9.default.mkdirSync(import_path5.default.dirname(absolute), { recursive: true });
|
|
13214
13219
|
return absolute;
|
|
13215
13220
|
}
|
|
13216
13221
|
/**
|
|
@@ -13222,7 +13227,7 @@ var FilenameResolver = class {
|
|
|
13222
13227
|
* // → '/out/2026-report-executive.pdf'
|
|
13223
13228
|
*/
|
|
13224
13229
|
injectTemplateSuffix(outputPath, suffix) {
|
|
13225
|
-
const ext =
|
|
13230
|
+
const ext = import_path5.default.extname(outputPath);
|
|
13226
13231
|
const base = outputPath.slice(0, outputPath.length - ext.length);
|
|
13227
13232
|
return `${base}-${suffix}${ext}`;
|
|
13228
13233
|
}
|
|
@@ -13281,7 +13286,12 @@ var PRESETS = {
|
|
|
13281
13286
|
// src/pdf/FailurePaginator.ts
|
|
13282
13287
|
init_cjs_shims();
|
|
13283
13288
|
var import_fs10 = __toESM(require("fs"));
|
|
13284
|
-
var
|
|
13289
|
+
var import_path6 = __toESM(require("path"));
|
|
13290
|
+
function sidecarPathFor(pdfPath) {
|
|
13291
|
+
const dir = import_path6.default.dirname(pdfPath);
|
|
13292
|
+
const base = import_path6.default.basename(pdfPath, import_path6.default.extname(pdfPath));
|
|
13293
|
+
return import_path6.default.join(dir, `${base}-failures.json`);
|
|
13294
|
+
}
|
|
13285
13295
|
var FailurePaginator = class {
|
|
13286
13296
|
async paginate(failures, maxInline, pdfOutputPath) {
|
|
13287
13297
|
if (failures.length <= maxInline) {
|
|
@@ -13289,7 +13299,7 @@ var FailurePaginator = class {
|
|
|
13289
13299
|
}
|
|
13290
13300
|
const inline = failures.slice(0, maxInline);
|
|
13291
13301
|
const overflow = failures.slice(maxInline);
|
|
13292
|
-
const sidecarPath =
|
|
13302
|
+
const sidecarPath = sidecarPathFor(pdfOutputPath);
|
|
13293
13303
|
try {
|
|
13294
13304
|
await import_fs10.default.promises.writeFile(
|
|
13295
13305
|
sidecarPath,
|
|
@@ -13297,7 +13307,7 @@ var FailurePaginator = class {
|
|
|
13297
13307
|
"utf8"
|
|
13298
13308
|
);
|
|
13299
13309
|
logger.info(
|
|
13300
|
-
`Emitted ${overflow.length} overflow failures \u2192 ${
|
|
13310
|
+
`Emitted ${overflow.length} overflow failures \u2192 ${import_path6.default.basename(sidecarPath)}`
|
|
13301
13311
|
);
|
|
13302
13312
|
} catch (e) {
|
|
13303
13313
|
logger.warn(`Could not write failure sidecar: ${e.message}`);
|
|
@@ -13305,11 +13315,6 @@ var FailurePaginator = class {
|
|
|
13305
13315
|
}
|
|
13306
13316
|
return { inline, overflowCount: overflow.length, sidecarPath };
|
|
13307
13317
|
}
|
|
13308
|
-
sidecarPathFor(pdfPath) {
|
|
13309
|
-
const dir = import_path7.default.dirname(pdfPath);
|
|
13310
|
-
const base = import_path7.default.basename(pdfPath, import_path7.default.extname(pdfPath));
|
|
13311
|
-
return import_path7.default.join(dir, `${base}-failures.json`);
|
|
13312
|
-
}
|
|
13313
13318
|
/**
|
|
13314
13319
|
* Drops the inlined image bytes — a 150-entry overflow would otherwise
|
|
13315
13320
|
* produce a 40 MB JSON. The path still references the on-disk screenshot
|
|
@@ -13324,6 +13329,21 @@ var FailurePaginator = class {
|
|
|
13324
13329
|
}
|
|
13325
13330
|
};
|
|
13326
13331
|
|
|
13332
|
+
// src/pdf/sidecar-crypto.ts
|
|
13333
|
+
init_cjs_shims();
|
|
13334
|
+
var import_crypto7 = __toESM(require("crypto"));
|
|
13335
|
+
var MAGIC = Buffer.from("RFENC1");
|
|
13336
|
+
var SALT_LEN = 16;
|
|
13337
|
+
var IV_LEN = 12;
|
|
13338
|
+
function encryptSidecar(plain, password) {
|
|
13339
|
+
const salt = import_crypto7.default.randomBytes(SALT_LEN);
|
|
13340
|
+
const key = import_crypto7.default.scryptSync(password, salt, 32, { N: 16384, r: 8, p: 1 });
|
|
13341
|
+
const iv = import_crypto7.default.randomBytes(IV_LEN);
|
|
13342
|
+
const cipher = import_crypto7.default.createCipheriv("aes-256-gcm", key, iv);
|
|
13343
|
+
const enc = Buffer.concat([cipher.update(plain), cipher.final()]);
|
|
13344
|
+
return Buffer.concat([MAGIC, salt, iv, cipher.getAuthTag(), enc]);
|
|
13345
|
+
}
|
|
13346
|
+
|
|
13327
13347
|
// src/pdf/PdfGenerator.ts
|
|
13328
13348
|
var PdfGenerator = class {
|
|
13329
13349
|
constructor() {
|
|
@@ -13421,7 +13441,7 @@ var PdfGenerator = class {
|
|
|
13421
13441
|
const templateId = source.kind === "builtin" ? source.id : "detailed";
|
|
13422
13442
|
const templateData = { ...data, meta: { ...data.meta, template: templateId } };
|
|
13423
13443
|
const templateOptions = { ...options, template: templateId };
|
|
13424
|
-
await this.renderPdf(templateData, templateOptions, source, compression, outputPath, page);
|
|
13444
|
+
let { sidecarPath } = await this.renderPdf(templateData, templateOptions, source, compression, outputPath, page);
|
|
13425
13445
|
const capBytes = (options.maxFileSizeMb ?? 0) * 1024 * 1024;
|
|
13426
13446
|
if (capBytes > 0) {
|
|
13427
13447
|
const sizeBytes = (await import_fs11.default.promises.stat(outputPath)).size;
|
|
@@ -13431,7 +13451,8 @@ var PdfGenerator = class {
|
|
|
13431
13451
|
logger.warn(
|
|
13432
13452
|
`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}).`
|
|
13433
13453
|
);
|
|
13434
|
-
await this.renderPdf(templateData, templateOptions, source, tighter, outputPath, page);
|
|
13454
|
+
const retuned = await this.renderPdf(templateData, templateOptions, source, tighter, outputPath, page);
|
|
13455
|
+
sidecarPath = retuned.sidecarPath ?? sidecarPath;
|
|
13435
13456
|
const finalSize = (await import_fs11.default.promises.stat(outputPath)).size;
|
|
13436
13457
|
if (finalSize > capBytes) {
|
|
13437
13458
|
logger.warn(
|
|
@@ -13443,7 +13464,36 @@ var PdfGenerator = class {
|
|
|
13443
13464
|
}
|
|
13444
13465
|
}
|
|
13445
13466
|
if (options.pdfPassword) {
|
|
13446
|
-
|
|
13467
|
+
let pdfEncrypted = false;
|
|
13468
|
+
try {
|
|
13469
|
+
await this.pdfEncryptor.encrypt(outputPath, options.pdfPassword);
|
|
13470
|
+
pdfEncrypted = true;
|
|
13471
|
+
if (sidecarPath && import_fs11.default.existsSync(sidecarPath)) {
|
|
13472
|
+
const blob = encryptSidecar(await import_fs11.default.promises.readFile(sidecarPath), options.pdfPassword);
|
|
13473
|
+
await import_fs11.default.promises.writeFile(`${sidecarPath}.enc`, blob);
|
|
13474
|
+
await import_fs11.default.promises.unlink(sidecarPath);
|
|
13475
|
+
logger.info(`Overflow sidecar encrypted \u2192 ${import_path7.default.basename(sidecarPath)}.enc`);
|
|
13476
|
+
} else {
|
|
13477
|
+
const staleSidecarPath = sidecarPathFor(outputPath);
|
|
13478
|
+
if (import_fs11.default.existsSync(staleSidecarPath)) {
|
|
13479
|
+
import_fs11.default.rmSync(staleSidecarPath, { force: true });
|
|
13480
|
+
logger.info(`Removed stale plaintext sidecar: ${import_path7.default.basename(staleSidecarPath)}`);
|
|
13481
|
+
}
|
|
13482
|
+
}
|
|
13483
|
+
} catch (err) {
|
|
13484
|
+
const sidecarTargets = sidecarPath ? [sidecarPath, `${sidecarPath}.enc`] : [];
|
|
13485
|
+
const cleanupTargets = pdfEncrypted ? sidecarTargets : [outputPath, ...sidecarTargets];
|
|
13486
|
+
for (const target of cleanupTargets) {
|
|
13487
|
+
try {
|
|
13488
|
+
import_fs11.default.rmSync(target, { force: true });
|
|
13489
|
+
} catch (cleanupErr) {
|
|
13490
|
+
logger.warn(
|
|
13491
|
+
`Could not remove leftover file after encryption failure: ${target} (${cleanupErr.message})`
|
|
13492
|
+
);
|
|
13493
|
+
}
|
|
13494
|
+
}
|
|
13495
|
+
throw err;
|
|
13496
|
+
}
|
|
13447
13497
|
}
|
|
13448
13498
|
const finalSizeMb = fmtMb((await import_fs11.default.promises.stat(outputPath)).size);
|
|
13449
13499
|
logger.info(`PDF report generated: ${outputPath} (${finalSizeMb})`);
|
|
@@ -13458,17 +13508,17 @@ var PdfGenerator = class {
|
|
|
13458
13508
|
if (options.templatePath && options.templatePath.length > 0) {
|
|
13459
13509
|
const byAbsolutePath = /* @__PURE__ */ new Map();
|
|
13460
13510
|
for (const p of options.templatePath) {
|
|
13461
|
-
const absolutePath =
|
|
13511
|
+
const absolutePath = import_path7.default.isAbsolute(p) ? p : import_path7.default.resolve(process.cwd(), p);
|
|
13462
13512
|
if (!byAbsolutePath.has(absolutePath)) byAbsolutePath.set(absolutePath, p);
|
|
13463
13513
|
}
|
|
13464
13514
|
const labelCounts = /* @__PURE__ */ new Map();
|
|
13465
13515
|
for (const rawPath of byAbsolutePath.values()) {
|
|
13466
|
-
const label =
|
|
13516
|
+
const label = import_path7.default.basename(rawPath, ".hbs");
|
|
13467
13517
|
labelCounts.set(label, (labelCounts.get(label) ?? 0) + 1);
|
|
13468
13518
|
}
|
|
13469
13519
|
const seenPerLabel = /* @__PURE__ */ new Map();
|
|
13470
13520
|
return Array.from(byAbsolutePath.entries()).map(([absolutePath, rawPath]) => {
|
|
13471
|
-
const baseLabel =
|
|
13521
|
+
const baseLabel = import_path7.default.basename(rawPath, ".hbs");
|
|
13472
13522
|
let label = baseLabel;
|
|
13473
13523
|
if ((labelCounts.get(baseLabel) ?? 0) > 1) {
|
|
13474
13524
|
const n = (seenPerLabel.get(baseLabel) ?? 0) + 1;
|
|
@@ -13528,7 +13578,7 @@ var PdfGenerator = class {
|
|
|
13528
13578
|
...data,
|
|
13529
13579
|
failures: paginated.inline,
|
|
13530
13580
|
overflowFailureCount: paginated.overflowCount,
|
|
13531
|
-
overflowSidecarName: paginated.sidecarPath ?
|
|
13581
|
+
overflowSidecarName: paginated.sidecarPath ? import_path7.default.basename(paginated.sidecarPath) + (options.pdfPassword ? ".enc" : "") : void 0
|
|
13532
13582
|
};
|
|
13533
13583
|
for (const f of renderData.failures) f.screenshotBase64 = void 0;
|
|
13534
13584
|
if (options.includeScreenshots !== false) {
|
|
@@ -13553,7 +13603,7 @@ var PdfGenerator = class {
|
|
|
13553
13603
|
await page.goto(fileUrl, { waitUntil: "domcontentloaded", timeout: 6e4 });
|
|
13554
13604
|
const showCharts = source.kind === "builtin" ? resolveSections(source.id, options.sections).charts : true;
|
|
13555
13605
|
await this.chartRenderer.waitForCharts(page, showCharts);
|
|
13556
|
-
logger.info(`Generating PDF \u2192 ${
|
|
13606
|
+
logger.info(`Generating PDF \u2192 ${import_path7.default.relative(process.cwd(), outputPath)}`);
|
|
13557
13607
|
await page.pdf({
|
|
13558
13608
|
path: outputPath,
|
|
13559
13609
|
format: "A4",
|
|
@@ -13563,19 +13613,129 @@ var PdfGenerator = class {
|
|
|
13563
13613
|
} finally {
|
|
13564
13614
|
await this.htmlWriter.cleanup();
|
|
13565
13615
|
}
|
|
13616
|
+
return { sidecarPath: paginated.overflowCount > 0 ? paginated.sidecarPath : void 0 };
|
|
13566
13617
|
}
|
|
13567
13618
|
};
|
|
13568
13619
|
function fmtMb(bytes) {
|
|
13569
13620
|
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
13570
13621
|
}
|
|
13571
13622
|
|
|
13623
|
+
// src/redact/Redactor.ts
|
|
13624
|
+
init_cjs_shims();
|
|
13625
|
+
var Redactor = class {
|
|
13626
|
+
constructor(opts) {
|
|
13627
|
+
/** Indexes of user patterns disabled after throwing (warned once each). */
|
|
13628
|
+
this.disabled = /* @__PURE__ */ new Set();
|
|
13629
|
+
this.maskRaw = opts.mask;
|
|
13630
|
+
this.maskReplacement = opts.mask.replace(/\$/g, "$$$$");
|
|
13631
|
+
this.useBuiltins = opts.builtins;
|
|
13632
|
+
this.userPatterns = opts.patterns.map((p) => new RegExp(p, "g"));
|
|
13633
|
+
}
|
|
13634
|
+
redactText(s) {
|
|
13635
|
+
if (!s) return s;
|
|
13636
|
+
let out = s;
|
|
13637
|
+
if (this.useBuiltins) out = this.applyBuiltins(out);
|
|
13638
|
+
for (let i = 0; i < this.userPatterns.length; i++) {
|
|
13639
|
+
if (this.disabled.has(i)) continue;
|
|
13640
|
+
try {
|
|
13641
|
+
out = out.replace(this.userPatterns[i], this.maskReplacement);
|
|
13642
|
+
} catch (err) {
|
|
13643
|
+
this.disabled.add(i);
|
|
13644
|
+
logger.warn(
|
|
13645
|
+
`redact: custom pattern #${i + 1} threw and is disabled for this run: ${err.message}`
|
|
13646
|
+
);
|
|
13647
|
+
}
|
|
13648
|
+
}
|
|
13649
|
+
return out;
|
|
13650
|
+
}
|
|
13651
|
+
applyBuiltins(s) {
|
|
13652
|
+
const M = this.maskRaw;
|
|
13653
|
+
const MR = this.maskReplacement;
|
|
13654
|
+
return s.replace(/(\b[a-z][\w+.-]{0,63}:\/\/[^/\s:@]{1,512}:)([^@\s/]{1,512})(?=@)/gi, `$1${MR}`).replace(
|
|
13655
|
+
/\b(?:(authorization\s*[:=]\s*basic\s+)([A-Za-z0-9._~+/=-]{8,})|((?:authorization\s*[:=]\s*)?bearer\s+)([A-Za-z0-9._~+/=-]{8,}))/gi,
|
|
13656
|
+
(fullMatch, basicPrefix, _basicToken, bearerPrefix, bearerToken) => {
|
|
13657
|
+
if (basicPrefix) return `${basicPrefix}${M}`;
|
|
13658
|
+
if (bearerToken && /[^a-z]/.test(bearerToken)) return `${bearerPrefix}${M}`;
|
|
13659
|
+
return fullMatch;
|
|
13660
|
+
}
|
|
13661
|
+
).replace(/\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\b/g, MR).replace(
|
|
13662
|
+
/\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,
|
|
13663
|
+
MR
|
|
13664
|
+
).replace(
|
|
13665
|
+
/\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,
|
|
13666
|
+
(_m, key, sep, q, _quotedVal, _plainVal) => q ? `${key}${sep}${q}${M}${q}` : `${key}${sep}${M}`
|
|
13667
|
+
).replace(
|
|
13668
|
+
/\b(fill|type|press)\b([^\n]{0,80}?\b(?:password|secret|token|pass|passcode|pin|otp)\b[^\n]{0,80}?\s)(["'])((?:(?!\3).)+)\3/gi,
|
|
13669
|
+
(_m, action, mid, q) => `${action}${mid}${q}${M}${q}`
|
|
13670
|
+
).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) => {
|
|
13671
|
+
const before = offset > 0 ? str[offset - 1] : " ";
|
|
13672
|
+
const after = offset + m.length < str.length ? str[offset + m.length] : " ";
|
|
13673
|
+
const beforeIsWord = /[A-Za-z0-9_]/.test(before);
|
|
13674
|
+
const afterIsWord = /[A-Za-z0-9_]/.test(after);
|
|
13675
|
+
if (!beforeIsWord && !afterIsWord && /\d/.test(m) && /[A-Za-z]/.test(m)) {
|
|
13676
|
+
return M;
|
|
13677
|
+
}
|
|
13678
|
+
return m;
|
|
13679
|
+
});
|
|
13680
|
+
}
|
|
13681
|
+
};
|
|
13682
|
+
|
|
13683
|
+
// src/redact/redact-report.ts
|
|
13684
|
+
init_cjs_shims();
|
|
13685
|
+
function redactReportData(data, r) {
|
|
13686
|
+
const rd = (s) => s === void 0 ? void 0 : r.redactText(s);
|
|
13687
|
+
for (const f of data.failures) {
|
|
13688
|
+
f.testTitle = r.redactText(f.testTitle);
|
|
13689
|
+
f.suitePath = f.suitePath.map((s) => r.redactText(s));
|
|
13690
|
+
f.error.message = r.redactText(f.error.message);
|
|
13691
|
+
f.error.stack = rd(f.error.stack);
|
|
13692
|
+
if (f.execution) {
|
|
13693
|
+
f.execution.steps?.forEach((s) => redactStep(s, r));
|
|
13694
|
+
if (f.execution.failingStep) redactStep(f.execution.failingStep, r);
|
|
13695
|
+
if (f.execution.console) {
|
|
13696
|
+
f.execution.console.stdout = f.execution.console.stdout.map((l) => r.redactText(l));
|
|
13697
|
+
f.execution.console.stderr = f.execution.console.stderr.map((l) => r.redactText(l));
|
|
13698
|
+
}
|
|
13699
|
+
}
|
|
13700
|
+
}
|
|
13701
|
+
for (const p of data.projects) p.suites.forEach((s) => redactSuite(s, r));
|
|
13702
|
+
if (data.analysis) {
|
|
13703
|
+
for (const c of data.analysis.clusters) {
|
|
13704
|
+
c.representativeMessage = r.redactText(c.representativeMessage);
|
|
13705
|
+
c.tests = c.tests.map((t) => r.redactText(t));
|
|
13706
|
+
}
|
|
13707
|
+
data.analysis.oneLiner = r.redactText(data.analysis.oneLiner);
|
|
13708
|
+
}
|
|
13709
|
+
data.classifications?.forEach((c) => c.message = r.redactText(c.message));
|
|
13710
|
+
data.briefSentence = rd(data.briefSentence);
|
|
13711
|
+
data.charts.suiteResults.forEach((s) => s.label = r.redactText(s.label));
|
|
13712
|
+
data.charts.flakinessTable?.forEach((row) => row.title = r.redactText(row.title));
|
|
13713
|
+
data.environment.buildUrl = r.redactText(data.environment.buildUrl);
|
|
13714
|
+
}
|
|
13715
|
+
function redactStep(s, r) {
|
|
13716
|
+
s.title = r.redactText(s.title);
|
|
13717
|
+
if (s.errorMessage !== void 0) s.errorMessage = r.redactText(s.errorMessage);
|
|
13718
|
+
}
|
|
13719
|
+
function redactSuite(s, r) {
|
|
13720
|
+
s.title = r.redactText(s.title);
|
|
13721
|
+
for (const t of s.tests) {
|
|
13722
|
+
t.title = r.redactText(t.title);
|
|
13723
|
+
t.fullTitle = r.redactText(t.fullTitle);
|
|
13724
|
+
t.tags = t.tags.map((tag) => r.redactText(tag));
|
|
13725
|
+
t.annotations.forEach((a) => {
|
|
13726
|
+
if (a.description !== void 0) a.description = r.redactText(a.description);
|
|
13727
|
+
});
|
|
13728
|
+
}
|
|
13729
|
+
s.suites.forEach((child) => redactSuite(child, r));
|
|
13730
|
+
}
|
|
13731
|
+
|
|
13572
13732
|
// src/reporter.ts
|
|
13573
|
-
var
|
|
13733
|
+
var import_path12 = __toESM(require("path"));
|
|
13574
13734
|
|
|
13575
13735
|
// src/history/HistoryManager.ts
|
|
13576
13736
|
init_cjs_shims();
|
|
13577
13737
|
var import_fs12 = __toESM(require("fs"));
|
|
13578
|
-
var
|
|
13738
|
+
var import_path8 = __toESM(require("path"));
|
|
13579
13739
|
var HistoryManager = class {
|
|
13580
13740
|
constructor(filePath) {
|
|
13581
13741
|
this.filePath = filePath;
|
|
@@ -13596,7 +13756,7 @@ var HistoryManager = class {
|
|
|
13596
13756
|
}
|
|
13597
13757
|
append(entry, maxSize) {
|
|
13598
13758
|
try {
|
|
13599
|
-
import_fs12.default.mkdirSync(
|
|
13759
|
+
import_fs12.default.mkdirSync(import_path8.default.dirname(this.filePath), { recursive: true });
|
|
13600
13760
|
const next = {
|
|
13601
13761
|
version: 1,
|
|
13602
13762
|
entries: [entry, ...this.read()].slice(0, maxSize)
|
|
@@ -13694,14 +13854,14 @@ init_cjs_shims();
|
|
|
13694
13854
|
// src/notify/NotificationSender.ts
|
|
13695
13855
|
init_cjs_shims();
|
|
13696
13856
|
var import_promises2 = require("fs/promises");
|
|
13697
|
-
var
|
|
13857
|
+
var import_path11 = require("path");
|
|
13698
13858
|
|
|
13699
13859
|
// src/notify/formatters/slack.ts
|
|
13700
13860
|
init_cjs_shims();
|
|
13701
13861
|
|
|
13702
13862
|
// src/notify/formatters/shared.ts
|
|
13703
13863
|
init_cjs_shims();
|
|
13704
|
-
var
|
|
13864
|
+
var import_path9 = require("path");
|
|
13705
13865
|
function statusLabel(verdict) {
|
|
13706
13866
|
switch (verdict) {
|
|
13707
13867
|
case "passed":
|
|
@@ -13725,7 +13885,7 @@ function countsLine(stats) {
|
|
|
13725
13885
|
return parts.join(" \xB7 ");
|
|
13726
13886
|
}
|
|
13727
13887
|
function reportName(pdfPaths, reportTitle) {
|
|
13728
|
-
if (pdfPaths.length > 0) return (0,
|
|
13888
|
+
if (pdfPaths.length > 0) return (0, import_path9.basename)(pdfPaths[0]);
|
|
13729
13889
|
return reportTitle || "ReportForge";
|
|
13730
13890
|
}
|
|
13731
13891
|
|
|
@@ -13825,7 +13985,7 @@ ${name}`,
|
|
|
13825
13985
|
// src/notify/EmailSender.ts
|
|
13826
13986
|
init_cjs_shims();
|
|
13827
13987
|
var import_promises = require("fs/promises");
|
|
13828
|
-
var
|
|
13988
|
+
var import_path10 = require("path");
|
|
13829
13989
|
|
|
13830
13990
|
// src/notify/formatters/email.ts
|
|
13831
13991
|
init_cjs_shims();
|
|
@@ -13899,7 +14059,7 @@ var EmailSender = class {
|
|
|
13899
14059
|
const pdfPath = pdfPaths[0];
|
|
13900
14060
|
try {
|
|
13901
14061
|
const content = await (0, import_promises.readFile)(pdfPath);
|
|
13902
|
-
body["attachments"] = [{ filename: (0,
|
|
14062
|
+
body["attachments"] = [{ filename: (0, import_path10.basename)(pdfPath), content: content.toString("base64") }];
|
|
13903
14063
|
} catch (err) {
|
|
13904
14064
|
logger.warn(`[notify] email: could not read PDF for attachment: ${err instanceof Error ? err.message : String(err)}`);
|
|
13905
14065
|
}
|
|
@@ -14025,7 +14185,7 @@ var NotificationSender = class {
|
|
|
14025
14185
|
}
|
|
14026
14186
|
const form = new FormData();
|
|
14027
14187
|
form.append("payload_json", JSON.stringify(payload));
|
|
14028
|
-
form.append("files[0]", new Blob([new Uint8Array(file)], { type: "application/pdf" }), (0,
|
|
14188
|
+
form.append("files[0]", new Blob([new Uint8Array(file)], { type: "application/pdf" }), (0, import_path11.basename)(pdfPath));
|
|
14029
14189
|
const controller = new AbortController();
|
|
14030
14190
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
14031
14191
|
try {
|
|
@@ -14099,6 +14259,10 @@ var LiveStreamer = class {
|
|
|
14099
14259
|
}
|
|
14100
14260
|
enqueueTest(update) {
|
|
14101
14261
|
if (!this.enabled) return;
|
|
14262
|
+
if (this.config.redact) {
|
|
14263
|
+
if (update.title !== void 0) update.title = this.config.redact(update.title);
|
|
14264
|
+
if (update.currentStep !== void 0) update.currentStep = this.config.redact(update.currentStep);
|
|
14265
|
+
}
|
|
14102
14266
|
const prevRetry = this.testStates.get(update.id)?.retry;
|
|
14103
14267
|
this.testStates.set(update.id, { status: update.status, retry: update.retry ?? prevRetry });
|
|
14104
14268
|
this.testBuffer.push(update);
|
|
@@ -14107,6 +14271,10 @@ var LiveStreamer = class {
|
|
|
14107
14271
|
}
|
|
14108
14272
|
enqueueEvent(event) {
|
|
14109
14273
|
if (!this.enabled) return;
|
|
14274
|
+
if (this.config.redact) {
|
|
14275
|
+
if (event.title !== void 0) event.title = this.config.redact(event.title);
|
|
14276
|
+
if (event.text !== void 0) event.text = this.config.redact(event.text);
|
|
14277
|
+
}
|
|
14110
14278
|
event.id = `${this.config.shardKey}:${this.eventSeq++}`;
|
|
14111
14279
|
this.eventBuffer.push(event);
|
|
14112
14280
|
this.capPreLicenseBuffer();
|
|
@@ -14413,7 +14581,10 @@ var PdfReporter = class {
|
|
|
14413
14581
|
this.options = parseOptions(rawOptions);
|
|
14414
14582
|
setLogLevel(this.options.logLevel);
|
|
14415
14583
|
this.dataCollector = new DataCollector(this.options.capture);
|
|
14416
|
-
this.
|
|
14584
|
+
if (this.options.redact.enabled) {
|
|
14585
|
+
this.redactor = new Redactor(this.options.redact);
|
|
14586
|
+
}
|
|
14587
|
+
this.version = true ? "0.23.0" : "0.x";
|
|
14417
14588
|
logger.info(`@reportforge/playwright-pdf v${this.version} initialised`);
|
|
14418
14589
|
this.licenseClient = new LicenseClient({
|
|
14419
14590
|
licenseKey: this.options.licenseKey,
|
|
@@ -14504,6 +14675,9 @@ var PdfReporter = class {
|
|
|
14504
14675
|
const reportData = this._buildReportData(collected, licenseInfo);
|
|
14505
14676
|
runFailureAnalysis(reportData, this.options, this.cwd);
|
|
14506
14677
|
reportData.briefSentence = buildBriefSentence(reportData.stats, reportData.analysis, reportData.charts.trend);
|
|
14678
|
+
if (this.redactor) {
|
|
14679
|
+
redactReportData(reportData, this.redactor);
|
|
14680
|
+
}
|
|
14507
14681
|
let pdfPaths = [];
|
|
14508
14682
|
try {
|
|
14509
14683
|
pdfPaths = await this.pdfGenerator.generateAll(reportData, this.options, licenseInfo);
|
|
@@ -14535,7 +14709,10 @@ var PdfReporter = class {
|
|
|
14535
14709
|
const historyPath = resolveHistoryPath(this.options, this.cwd);
|
|
14536
14710
|
const hm = new HistoryManager(historyPath);
|
|
14537
14711
|
const suffix = Math.random().toString(36).padEnd(8, "0").slice(2, 6);
|
|
14538
|
-
const
|
|
14712
|
+
const redactor = this.redactor;
|
|
14713
|
+
const flakyTests = collectFlakyTests(collected.projects).map(
|
|
14714
|
+
(t) => redactor ? { ...t, title: redactor.redactText(t.title) } : t
|
|
14715
|
+
);
|
|
14539
14716
|
const entry = {
|
|
14540
14717
|
runId: `${(/* @__PURE__ */ new Date()).toISOString()}-${suffix}`,
|
|
14541
14718
|
timestamp: Date.now(),
|
|
@@ -14675,6 +14852,7 @@ var PdfReporter = class {
|
|
|
14675
14852
|
const token = computeWatchToken(runId, this.options.licenseKey);
|
|
14676
14853
|
const watchUrl = token ? buildWatchUrl(serverUrl, runId, token) : void 0;
|
|
14677
14854
|
const env = detectCiEnv();
|
|
14855
|
+
if (this.redactor && env?.buildUrl) env.buildUrl = this.redactor.redactText(env.buildUrl);
|
|
14678
14856
|
if (watchUrl) {
|
|
14679
14857
|
void this.announceLive(watchUrl, env, shard).catch(() => {
|
|
14680
14858
|
});
|
|
@@ -14689,6 +14867,7 @@ var PdfReporter = class {
|
|
|
14689
14867
|
shardKey: this.liveShardKey,
|
|
14690
14868
|
shardTotal: shard?.total ?? null,
|
|
14691
14869
|
license: () => this.license(),
|
|
14870
|
+
redact: this.redactor ? (s) => this.redactor.redactText(s) : void 0,
|
|
14692
14871
|
env,
|
|
14693
14872
|
flushMs: live.flushMs
|
|
14694
14873
|
});
|
|
@@ -14730,7 +14909,7 @@ var PdfReporter = class {
|
|
|
14730
14909
|
}
|
|
14731
14910
|
resolveProjectName() {
|
|
14732
14911
|
try {
|
|
14733
|
-
const pkgPath =
|
|
14912
|
+
const pkgPath = import_path12.default.resolve(process.cwd(), "package.json");
|
|
14734
14913
|
const pkg = require(pkgPath);
|
|
14735
14914
|
return pkg.name ?? "Playwright Tests";
|
|
14736
14915
|
} catch {
|
|
@@ -14780,9 +14959,9 @@ function deriveVerdict(stats) {
|
|
|
14780
14959
|
}
|
|
14781
14960
|
function resolveHistoryPath(options, cwd) {
|
|
14782
14961
|
if (options.historyFile) {
|
|
14783
|
-
return
|
|
14962
|
+
return import_path12.default.isAbsolute(options.historyFile) ? options.historyFile : import_path12.default.resolve(cwd, options.historyFile);
|
|
14784
14963
|
}
|
|
14785
|
-
return
|
|
14964
|
+
return import_path12.default.join(resolveProjectDir(options, cwd), "history.json");
|
|
14786
14965
|
}
|
|
14787
14966
|
function flattenSuiteNodes(suites) {
|
|
14788
14967
|
return suites.flatMap((s) => [s, ...flattenSuiteNodes(s.suites)]);
|