monocart-coverage-reports 3.1.0 → 3.2.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.
@@ -0,0 +1,115 @@
1
+ import path from 'node:path';
2
+ import { Util } from '../utils/util.js';
3
+ import { createTestResultsCollector, getRunnerOptions } from './test-results.js';
4
+
5
+ // Also recognize `mcr -c config -- test targets -- runner-options` without
6
+ // falling through to the shell's unrelated `test` command. Only MCR options
7
+ // before the first separator are parsed in that form.
8
+ const getTestCommand = (args, subArgs) => {
9
+ if (args[0] === 'test') {
10
+ return {
11
+ targets: args.slice(1), runnerArgs: subArgs.slice(1)
12
+ };
13
+ }
14
+ if (args.length || subArgs[1] !== 'test') {
15
+ return;
16
+ }
17
+ const remaining = subArgs.slice(2);
18
+ const index = remaining.indexOf('--');
19
+ if (index === -1) {
20
+ return {
21
+ targets: remaining, runnerArgs: []
22
+ };
23
+ }
24
+ return {
25
+ targets: remaining.slice(0, index), runnerArgs: remaining.slice(index + 1)
26
+ };
27
+ };
28
+
29
+ // Node owns target parsing, discovery and validation. Preserve each argument.
30
+ const getTestTargets = (test) => {
31
+ if (!test || (Array.isArray(test) && !test.length)) {
32
+ Util.logInfo('No test targets configured; skipping tests.');
33
+ return;
34
+ }
35
+ return Array.isArray(test) ? test : [test];
36
+ };
37
+
38
+ const initTestResultsCollector = (coverageOptions, runnerArgs, childArgs) => {
39
+ // Read results before coverage generation, which may remove the cache.
40
+ const testResultsCollector = createTestResultsCollector(runnerArgs, coverageOptions.cacheDir);
41
+ // Internal options must stay before Node's end-of-options marker.
42
+ childArgs.splice(1 + getRunnerOptions(runnerArgs).length, 0, ... testResultsCollector.args);
43
+ return testResultsCollector;
44
+ };
45
+
46
+ const runTestEnd = async (testResultsCollector, onTestEnd) => {
47
+ if (!testResultsCollector) {
48
+ return 0;
49
+ }
50
+ try {
51
+ const metadata = testResultsCollector.readMetadata();
52
+ const failed = !metadata.complete;
53
+ if (failed) {
54
+ Util.logError('Node test report is incomplete');
55
+ }
56
+ let testResults;
57
+ if (typeof onTestEnd === 'function') {
58
+ testResults = testResultsCollector.read();
59
+ await onTestEnd(testResults);
60
+ }
61
+ // Publish only after a successful hook, so redaction reaches raw/debug
62
+ // artifacts. Never fall back to unredacted data if the hook throws.
63
+ testResultsCollector.publish(testResults);
64
+ return process.exitCode || (failed ? 1 : 0);
65
+ } catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ Util.logError(`Node test report collection or onTestEnd failed: ${message}`);
68
+ return 1;
69
+ } finally {
70
+ // Do not export unfinished reporter output through coverage raw reports.
71
+ testResultsCollector.dispose();
72
+ }
73
+ };
74
+
75
+ const createTestRunner = (coverageOptions, runnerArgs = []) => {
76
+ const targets = getTestTargets(coverageOptions.test);
77
+ if (!targets) {
78
+ return;
79
+ }
80
+ const childArgs = ['--test', ... runnerArgs, ... targets];
81
+ let testResultsCollector;
82
+ let onTestEnd;
83
+ return {
84
+ initCoverageDir: (nodeV8CoverageDir) => {
85
+ // Tests may spawn children with a different working directory.
86
+ process.env.NODE_V8_COVERAGE = path.resolve(nodeV8CoverageDir);
87
+ },
88
+ onClose: async (generateCoverage) => {
89
+ const testExitCode = await runTestEnd(testResultsCollector, onTestEnd);
90
+ try {
91
+ const report = await generateCoverage();
92
+ if (!report) {
93
+ throw new Error('No coverage report was generated');
94
+ }
95
+ } finally {
96
+ // A later coverage hook must not mask a test hook/collection failure.
97
+ if (testExitCode) {
98
+ process.exitCode = process.exitCode || testExitCode;
99
+ }
100
+ }
101
+ },
102
+ execute: async (executeSubprocess, onClose) => {
103
+ // Always collect results; onStart may still configure the optional hook.
104
+ onTestEnd = coverageOptions.onTestEnd;
105
+ testResultsCollector = initTestResultsCollector(coverageOptions, runnerArgs, childArgs);
106
+ try {
107
+ return await executeSubprocess(process.execPath, childArgs, onClose);
108
+ } finally {
109
+ testResultsCollector?.dispose();
110
+ }
111
+ }
112
+ };
113
+ };
114
+
115
+ export { getTestCommand, createTestRunner };
package/migrate-to-3.md CHANGED
@@ -158,6 +158,10 @@ Custom reporter interfaces are unchanged: Istanbul reporters implement `execute(
158
158
  - `mcr test` is a new **optional** command; you do not have to replace your existing test runner.
159
159
  - `outputFilter` is a new **optional** filter, disabled by default. You do not have to replace `entryFilter` / `sourceFilter` with it.
160
160
 
161
+ ### Review test-result artifacts when upgrading to 3.2.0+
162
+
163
+ Starting in 3.2.0, `mcr test` always collects structured test results, even without `onTestEnd`. Raw exports (including ZIP) and debug caches can now include `test-results-<id>.json`, containing test names, paths and error strings that may be sensitive. Review existing automatic artifact uploads and retention policies. Use [`onTestEnd`](README.md#ontestend--mcr-test-only) to redact persisted results before publication; terminal output and temporary files already written are not redacted. Raw imports still merge coverage only, not test results. Other commands and API calls do not collect these test results.
164
+
161
165
  ## Migration checklist
162
166
 
163
167
  - [ ] Upgrade Node.js in local, CI, container and relevant child-process environments.
package/package.json CHANGED
@@ -1,161 +1,161 @@
1
- {
2
- "name": "monocart-coverage-reports",
3
- "version": "3.1.0",
4
- "description": "A code coverage tool to generate native V8 reports or Istanbul reports.",
5
- "type": "module",
6
- "main": "./lib/index.js",
7
- "bin": {
8
- "mcr": "./lib/cli.js"
9
- },
10
- "exports": {
11
- ".": {
12
- "types": "./lib/index.d.ts",
13
- "default": "./lib/index.js"
14
- },
15
- "./register": "./lib/register/register.js",
16
- "./converter": "./lib/converter/converter.js",
17
- "./util": "./lib/utils/util.js",
18
- "./package.json": "./package.json",
19
- "./skills/*": "./skills/*"
20
- },
21
- "types": "./lib/index.d.ts",
22
- "scripts": {
23
- "dev": "vite",
24
- "build": "vite build",
25
- "postbuild": "npm run build-test",
26
- "build-test": "node ./test/build/build-test.js",
27
- "test-unit": "node --test \"test/unit/*.test.js\"",
28
- "test-unit-coverage": "node ./lib/cli.js test -c test/mcr.config.unit.js",
29
- "test-types": "tsc -p test/types/tsconfig.json",
30
- "test-runtime": "node ./scripts/test-runtime.js",
31
- "test-node-env": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage-env node ./test/test-node-env.js && node ./test/generate-report.js",
32
- "test-node-api": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage-api node ./test/test-node-api.js",
33
- "test-node-ins": "node ./test/test-node-ins.js",
34
- "test-node-cdp": "node --inspect=9229 ./test/test-node-cdp.js",
35
- "test-node-vm": "node ./test/test-node-vm.js",
36
- "test-node-koa": "node ./test/test-node-koa.js",
37
- "test-node-fgc": "node ./test/test-node-fgc.js",
38
- "test-node": "node ./test/test.js --node",
39
- "test-istanbul": "node ./test/test-istanbul.js",
40
- "test-v8": "node ./test/test-v8.js",
41
- "test-puppeteer": "node ./test/test-puppeteer.js",
42
- "test-css": "node ./test/test-css.js",
43
- "test-css-min": "node ./test/test-css-min.js",
44
- "test-minify": "node ./test/test-minify.js",
45
- "test-esbuild": "node ./test/test-esbuild.js",
46
- "test-rollup": "node ./test/test-rollup.js",
47
- "test-sections": "node ./test/test-sections.js",
48
- "test-swc": "node ./test/test-swc.js",
49
- "test-v8-and-istanbul": "node ./test/test-v8-and-istanbul.js",
50
- "test-browser": "node ./test/test.js --browser",
51
- "test-cli": "node ./lib/cli.js node ./test/specs/node.test.js -c test/mcr.config.cli.js",
52
- "test-demo": "node ./test/test-demo.js",
53
- "test-merge": "node ./test/test-merge.js",
54
- "test-merge-istanbul": "node ./test/test-merge-istanbul.js",
55
- "test-merge-v8": "node ./test/test-merge-v8.js",
56
- "test-merge-cli": "node ./lib/cli.js merge -r console-details,v8 --inputDir .temp/code-coverage/raw --outputDir .temp/merge-cli",
57
- "test-client": "node ./test/test-client.js",
58
- "test-cp": "node ./lib/cli.js node test/test-cp.mjs --outputDir .temp/test-cp",
59
- "test-all": "node ./test/test.js",
60
- "test-pr": "node --inspect=9230 ./test/test-pr.js",
61
- "test": "npm run test-unit && npm run test-types && node ./lib/cli.js npm run test-all -c test/mcr.config.mcr.js && node ./scripts/docs.js",
62
- "test:snap": "cross-env TEST_SNAPSHOT=true npm run test",
63
- "open": "node ./scripts/open.js",
64
- "eol": "git rm -rf --cached . && git reset --hard HEAD",
65
- "lint": "eslint src/ test/unit/ --fix && stylelint src/**/*.scss && npm run lint:readme",
66
- "lint:readme": "node ./scripts/lint-readme.js",
67
- "version": "npm run build",
68
- "publish": "node ./scripts/publish.js"
69
- },
70
- "files": [
71
- "lib",
72
- "README.zh-Hans.md",
73
- "migrate-to-3.md",
74
- "skills"
75
- ],
76
- "license": "MIT",
77
- "repository": {
78
- "type": "git",
79
- "url": "git+https://github.com/cenfun/monocart-coverage-reports.git"
80
- },
81
- "dependencies": {
82
- "@jridgewell/sourcemap-codec": "^1.6.0",
83
- "acorn": "^8.19.0",
84
- "acorn-loose": "^8.5.2",
85
- "acorn-walk": "^8.3.5",
86
- "commander": "^15.0.0",
87
- "console-grid": "^2.2.4",
88
- "convert-source-map": "^2.0.0",
89
- "eight-colors": "^1.3.3",
90
- "execa": "^10.2.1",
91
- "istanbul-lib-coverage": "^3.2.2",
92
- "istanbul-lib-report": "^3.0.1",
93
- "istanbul-reports": "^3.2.0",
94
- "lz-utils": "^2.1.1",
95
- "monocart-locator": "^1.0.6",
96
- "node-stream-zip": "^1.16.0",
97
- "ws": "^8.22.0",
98
- "yazl": "^3.3.1"
99
- },
100
- "devDependencies": {
101
- "@actions/core": "^3.0.1",
102
- "@actions/github": "^9.1.1",
103
- "@jsdevtools/coverage-istanbul-loader": "^3.0.5",
104
- "@rollup/plugin-commonjs": "^29.0.3",
105
- "@rollup/plugin-typescript": "^12.3.1",
106
- "@swc/core": "^1.16.20",
107
- "@types/node": "^26.6.5",
108
- "@vitejs/plugin-vue": "^6.0.10",
109
- "async-tick": "^1.0.2",
110
- "axios": "^1.20.0",
111
- "babel-loader": "^10.1.1",
112
- "babel-plugin-istanbul": "^8.0.0",
113
- "cross-env": "^10.1.0",
114
- "diff-sequences": "^29.6.3",
115
- "eight-colors": "^1.3.3",
116
- "esbuild": "^0.28.2",
117
- "eslint": "^10.13.0",
118
- "eslint-config-plus": "^2.1.0",
119
- "eslint-plugin-html": "^8.2.1",
120
- "eslint-plugin-vue": "^10.11.1",
121
- "find-up": "^8.0.0",
122
- "koa": "^3.2.1",
123
- "koa-static-resolver": "^1.1.0",
124
- "minimatch": "^10.2.6",
125
- "monocart-code-viewer": "^1.2.0",
126
- "monocart-formatter": "^3.0.1",
127
- "open": "^11.0.4",
128
- "playwright": "^1.64.1",
129
- "postcss": "^8.5.31",
130
- "puppeteer": "^25.16.0",
131
- "rollup": "^4.65.0",
132
- "rollup-plugin-visualizer": "^7.1.1",
133
- "sass": "^1.106.1",
134
- "stylelint": "^17.16.0",
135
- "stylelint-config-plus": "^1.2.0",
136
- "supports-color": "^11.0.0",
137
- "swc-loader": "^0.2.7",
138
- "ts-loader": "^9.6.2",
139
- "tslib": "^2.8.1",
140
- "tsx": "^4.23.15",
141
- "turbogrid": "^3.4.2",
142
- "typescript": "^6.0.3",
143
- "vine-ui": "^4.2.0",
144
- "vite": "^8.3.5",
145
- "vite-plugin-css-injected-by-js": "^5.0.2",
146
- "webpack": "^5.112.2"
147
- },
148
- "engines": {
149
- "node": ">=22.15.0"
150
- },
151
- "puppeteer": {
152
- "skipDownload": true
153
- },
154
- "starfall": {
155
- "outdate": {
156
- "exclude": [
157
- "typescript"
158
- ]
159
- }
160
- }
161
- }
1
+ {
2
+ "name": "monocart-coverage-reports",
3
+ "version": "3.2.1",
4
+ "description": "A code coverage tool to generate native V8 reports or Istanbul reports.",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "bin": {
8
+ "mcr": "./lib/cli.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./lib/index.d.ts",
13
+ "default": "./lib/index.js"
14
+ },
15
+ "./register": "./lib/register/register.js",
16
+ "./converter": "./lib/converter/converter.js",
17
+ "./util": "./lib/utils/util.js",
18
+ "./package.json": "./package.json",
19
+ "./skills/*": "./skills/*"
20
+ },
21
+ "types": "./lib/index.d.ts",
22
+ "scripts": {
23
+ "dev": "vite",
24
+ "build": "vite build",
25
+ "postbuild": "npm run build-test",
26
+ "build-test": "node ./test/build/build-test.js",
27
+ "test-unit": "node --test \"test/unit/*.test.js\"",
28
+ "test-unit-coverage": "node ./lib/cli.js test -c test/mcr.config.unit.js",
29
+ "test-types": "tsc -p test/types/tsconfig.json",
30
+ "test-runtime": "node ./scripts/test-runtime.js",
31
+ "test-node-env": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage-env node ./test/test-node-env.js && node ./test/generate-report.js",
32
+ "test-node-api": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage-api node ./test/test-node-api.js",
33
+ "test-node-ins": "node ./test/test-node-ins.js",
34
+ "test-node-cdp": "node --inspect=9229 ./test/test-node-cdp.js",
35
+ "test-node-vm": "node ./test/test-node-vm.js",
36
+ "test-node-koa": "node ./test/test-node-koa.js",
37
+ "test-node-fgc": "node ./test/test-node-fgc.js",
38
+ "test-node": "node ./test/test.js --node",
39
+ "test-istanbul": "node ./test/test-istanbul.js",
40
+ "test-v8": "node ./test/test-v8.js",
41
+ "test-puppeteer": "node ./test/test-puppeteer.js",
42
+ "test-css": "node ./test/test-css.js",
43
+ "test-css-min": "node ./test/test-css-min.js",
44
+ "test-minify": "node ./test/test-minify.js",
45
+ "test-esbuild": "node ./test/test-esbuild.js",
46
+ "test-rollup": "node ./test/test-rollup.js",
47
+ "test-sections": "node ./test/test-sections.js",
48
+ "test-swc": "node ./test/test-swc.js",
49
+ "test-v8-and-istanbul": "node ./test/test-v8-and-istanbul.js",
50
+ "test-browser": "node ./test/test.js --browser",
51
+ "test-cli": "node ./lib/cli.js node ./test/specs/node.test.js -c test/mcr.config.cli.js",
52
+ "test-demo": "node ./test/test-demo.js",
53
+ "test-merge": "node ./test/test-merge.js",
54
+ "test-merge-istanbul": "node ./test/test-merge-istanbul.js",
55
+ "test-merge-v8": "node ./test/test-merge-v8.js",
56
+ "test-merge-cli": "node ./lib/cli.js merge -r console-details,v8 --inputDir .temp/code-coverage/raw --outputDir .temp/merge-cli",
57
+ "test-client": "node ./test/test-client.js",
58
+ "test-cp": "node ./lib/cli.js node test/test-cp.mjs --outputDir .temp/test-cp",
59
+ "test-all": "node ./test/test.js",
60
+ "test-pr": "node --inspect=9230 ./test/test-pr.js",
61
+ "test": "npm run test-unit && npm run test-types && node ./lib/cli.js npm run test-all -c test/mcr.config.mcr.js && node ./scripts/docs.js",
62
+ "test:snap": "cross-env TEST_SNAPSHOT=true npm run test",
63
+ "open": "node ./scripts/open.js",
64
+ "eol": "git rm -rf --cached . && git reset --hard HEAD",
65
+ "lint": "eslint src/ test/unit/ --fix && stylelint src/**/*.scss && npm run lint:readme",
66
+ "lint:readme": "node ./scripts/lint-readme.js",
67
+ "version": "npm run build",
68
+ "publish": "node ./scripts/publish.js"
69
+ },
70
+ "files": [
71
+ "lib",
72
+ "README.zh-Hans.md",
73
+ "migrate-to-3.md",
74
+ "skills"
75
+ ],
76
+ "license": "MIT",
77
+ "repository": {
78
+ "type": "git",
79
+ "url": "git+https://github.com/cenfun/monocart-coverage-reports.git"
80
+ },
81
+ "dependencies": {
82
+ "@jridgewell/sourcemap-codec": "^1.6.0",
83
+ "acorn": "^8.19.0",
84
+ "acorn-loose": "^8.5.2",
85
+ "acorn-walk": "^8.3.5",
86
+ "commander": "^15.1.0",
87
+ "console-grid": "^2.2.4",
88
+ "convert-source-map": "^2.0.0",
89
+ "eight-colors": "^1.3.3",
90
+ "execa": "^10.2.1",
91
+ "istanbul-lib-coverage": "^3.2.2",
92
+ "istanbul-lib-report": "^3.0.1",
93
+ "istanbul-reports": "^3.2.0",
94
+ "lz-utils": "^2.1.1",
95
+ "monocart-locator": "^1.0.6",
96
+ "node-stream-zip": "^1.16.0",
97
+ "ws": "^8.22.1",
98
+ "yazl": "^3.3.1"
99
+ },
100
+ "devDependencies": {
101
+ "@actions/core": "^3.0.1",
102
+ "@actions/github": "^9.1.1",
103
+ "@jsdevtools/coverage-istanbul-loader": "^3.0.5",
104
+ "@rollup/plugin-commonjs": "^29.0.3",
105
+ "@rollup/plugin-typescript": "^12.3.1",
106
+ "@swc/core": "^1.16.22",
107
+ "@types/node": "^26.6.6",
108
+ "@vitejs/plugin-vue": "^6.0.10",
109
+ "async-tick": "^1.0.2",
110
+ "axios": "^1.21.0",
111
+ "babel-loader": "^10.1.1",
112
+ "babel-plugin-istanbul": "^8.0.0",
113
+ "cross-env": "^10.1.0",
114
+ "diff-sequences": "^29.6.3",
115
+ "eight-colors": "^1.3.3",
116
+ "esbuild": "^0.28.2",
117
+ "eslint": "^10.14.0",
118
+ "eslint-config-plus": "^2.1.0",
119
+ "eslint-plugin-html": "^8.2.1",
120
+ "eslint-plugin-vue": "^10.12.0",
121
+ "find-up": "^8.0.0",
122
+ "koa": "^3.3.0",
123
+ "koa-static-resolver": "^1.1.0",
124
+ "minimatch": "^11.0.5",
125
+ "monocart-code-viewer": "^1.2.0",
126
+ "monocart-formatter": "^3.0.1",
127
+ "open": "^11.0.4",
128
+ "playwright": "^1.65.0",
129
+ "postcss": "^8.5.32",
130
+ "puppeteer": "^25.16.0",
131
+ "rollup": "^4.68.0",
132
+ "rollup-plugin-visualizer": "^7.1.2",
133
+ "sass": "^1.108.1",
134
+ "stylelint": "^18.0.0",
135
+ "stylelint-config-plus": "^1.2.0",
136
+ "supports-color": "^11.0.0",
137
+ "swc-loader": "^0.2.7",
138
+ "ts-loader": "^9.6.2",
139
+ "tslib": "^2.9.1",
140
+ "tsx": "^4.23.15",
141
+ "turbogrid": "^3.4.2",
142
+ "typescript": "^6.0.3",
143
+ "vine-ui": "^4.2.1",
144
+ "vite": "^8.3.6",
145
+ "vite-plugin-css-injected-by-js": "^5.0.3",
146
+ "webpack": "^5.112.2"
147
+ },
148
+ "engines": {
149
+ "node": ">=22.15.0"
150
+ },
151
+ "puppeteer": {
152
+ "skipDownload": true
153
+ },
154
+ "starfall": {
155
+ "outdate": {
156
+ "exclude": [
157
+ "typescript"
158
+ ]
159
+ }
160
+ }
161
+ }
@@ -76,17 +76,22 @@ export default {
76
76
  - `test` is CLI-only and ignored by ordinary commands/API. Existing runners remain supported through `mcr npm run ...`.
77
77
  - Test failure still attempts report generation and retains a nonzero exit status. A report-generation failure must fail CI too. Validate both paths.
78
78
 
79
+ Starting in **3.2.0**, `mcr test` always collects structured test results, even without `onTestEnd`. Results follow the coverage cache lifecycle: normally cleaned after generation, retained in debug mode, or exported by `raw` (including ZIP). Raw imports merge coverage only, not test results. Test names, paths and error strings may contain secrets; review artifacts before uploading them. Use `onTestEnd` to redact persisted results before publication; this does not redact terminal output or temporary files already written.
80
+
79
81
  ## Hooks and shutdown
80
82
 
81
83
  | Hook | Scope / timing |
82
84
  | --- | --- |
83
85
  | `onStart(report)` | CLI child execution, before spawn |
86
+ | `onTestEnd(testResults)` | 3.2.0+, `mcr test` only: after result collection, before publication, `onReady` and coverage generation; awaited even on assertion failure |
84
87
  | `onReady(report, nodeV8CoverageDir, subprocess)` | CLI, after child exits, before reading inputs |
85
88
  | `onEntry(entry)` | V8 input processing; can mutate entry |
86
89
  | `onEnd(results)` | After report generation; suitable for thresholds |
87
90
 
88
91
  Hooks may be async. Use `onReady` for a concrete flush/readiness condition when necessary, not an arbitrary permanent delay. API integrations manage their own process lifecycle; they do not receive CLI-only hooks automatically. Preserve test/child failures even if cleanup or reporting also fails.
89
92
 
93
+ `onTestEnd` is optional and is not called for skipped runs, other commands or API calls. Mutate the result object to sanitize persisted raw/debug data. If it throws, test results are not published, the command fails, and coverage generation is still attempted. Missing/invalid reports fail without invoking the hook; incomplete reports are delivered with `complete: false` and also fail the command. See the [README hook contract](../../../README.md#ontestend--mcr-test-only) for the result schema and storage details. Save separate results outside `outputDir` if subsequent coverage cleanup could remove them.
94
+
90
95
  ## API use in CommonJS hosts
91
96
 
92
97
  MCR 3.x uses ESM, but a CommonJS host can use dynamic import without converting its entire application:
@@ -128,6 +128,8 @@ const options = {
128
128
  };
129
129
  ```
130
130
 
131
+ **Test-result privacy (3.2.0+):** `mcr test` adds `test-results-<id>.json` to raw exports (including ZIP) and retains it in debug caches, even without `onTestEnd`. Test names, paths and error strings can contain credentials or personal data. Review artifacts before uploading, and use [`onTestEnd`](cli.md#hooks-and-shutdown) to redact persisted results before publication. This does not redact terminal output or temporary files already written. Raw imports merge coverage only; test results are not merged.
132
+
131
133
  The relative raw directory becomes `coverage-reports/shard-1/raw`. Upload/download the **entire** raw directory, including source files. After every required job finishes, merge in a separate output directory:
132
134
 
133
135
  ```js