pi-jscpd 0.2.1 → 0.2.2
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/CHANGELOG.md +23 -1
- package/CONTRIBUTING.md +30 -2
- package/README.md +122 -55
- package/SECURITY.md +1 -1
- package/docs/adoption.md +131 -0
- package/docs/automatic-checkpoint.md +4 -3
- package/docs/benchmarks.md +78 -0
- package/docs/compatibility.md +54 -13
- package/docs/effect-architecture.md +2 -2
- package/docs/fallow-coexistence.md +1 -1
- package/docs/images/jscpd-findings.png +0 -0
- package/docs/m8-validation.md +389 -0
- package/docs/overlay-interaction.md +1 -1
- package/docs/release.md +10 -2
- package/package.json +9 -8
- package/scripts/check-compatibility.mjs +4 -4
- package/skills/jscpd/SKILL.md +1 -1
- package/src/jscpd-report.ts +19 -8
- package/src/presentation.ts +2 -2
package/src/jscpd-report.ts
CHANGED
|
@@ -24,14 +24,17 @@ export const JSCPD_STRUCTURED_REPORTER = "json";
|
|
|
24
24
|
export const JSCPD_STRUCTURED_REPORT_FILE_NAME = "jscpd-report.json";
|
|
25
25
|
|
|
26
26
|
const MAX_REPORT_BYTES = 16 * 1_024 * 1_024;
|
|
27
|
-
|
|
27
|
+
/** Retained, path-resolved pairs kept in session memory. jscpd totals may be larger. */
|
|
28
|
+
export const JSCPD_MAX_DECODED_CLONE_PAIRS = 1_000;
|
|
29
|
+
const MAX_CLONE_PAIRS = JSCPD_MAX_DECODED_CLONE_PAIRS;
|
|
28
30
|
const MAX_FORMATS = 256;
|
|
29
31
|
const MAX_PATH_BYTES = 4_096;
|
|
30
32
|
const MAX_FORMAT_BYTES = 128;
|
|
31
33
|
const MAX_DATE_BYTES = 128;
|
|
32
34
|
const MAX_U32 = 0xffff_ffff;
|
|
33
35
|
const MAX_JSON_DEPTH = 64;
|
|
34
|
-
|
|
36
|
+
/** Secondary to the 16 MiB report cap; large trees have many small JSON keys. */
|
|
37
|
+
const MAX_JSON_KEYS = 1_000_000;
|
|
35
38
|
const MAX_JSON_KEYS_PER_OBJECT = 2_048;
|
|
36
39
|
const PATH_RESOLUTION_CONCURRENCY = 16;
|
|
37
40
|
|
|
@@ -151,13 +154,14 @@ function parseReportBytes(bytes: Uint8Array): ParsedReport {
|
|
|
151
154
|
requiredProperty(topLevel, "duplicates", "invalid-top-level"),
|
|
152
155
|
"invalid-duplicates",
|
|
153
156
|
);
|
|
154
|
-
if (duplicates.length > MAX_CLONE_PAIRS) {
|
|
155
|
-
fail("limit-exceeded");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const clonePairs = duplicates.map(parseClonePair);
|
|
159
157
|
const statistics = parseStatistics(requiredProperty(topLevel, "statistics", "invalid-top-level"));
|
|
160
|
-
|
|
158
|
+
if (statistics.total.clones !== duplicates.length) {
|
|
159
|
+
fail("invalid-statistics");
|
|
160
|
+
}
|
|
161
|
+
const truncated = duplicates.length > MAX_CLONE_PAIRS;
|
|
162
|
+
const retained = truncated ? duplicates.slice(0, MAX_CLONE_PAIRS) : duplicates;
|
|
163
|
+
const clonePairs = retained.map(parseClonePair);
|
|
164
|
+
validateCloneStatistics(clonePairs, statistics, truncated);
|
|
161
165
|
return { statistics, clonePairs };
|
|
162
166
|
}
|
|
163
167
|
|
|
@@ -402,7 +406,14 @@ function parseStatisticsRow(value: unknown): JscpdStatisticsRow {
|
|
|
402
406
|
function validateCloneStatistics(
|
|
403
407
|
clonePairs: readonly UnresolvedClonePair[],
|
|
404
408
|
statistics: JscpdScanStatistics,
|
|
409
|
+
truncated: boolean,
|
|
405
410
|
): void {
|
|
411
|
+
if (truncated) {
|
|
412
|
+
if (clonePairs.length !== MAX_CLONE_PAIRS || statistics.total.clones < clonePairs.length) {
|
|
413
|
+
fail("invalid-statistics");
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
406
417
|
if (statistics.total.clones !== clonePairs.length) {
|
|
407
418
|
fail("invalid-statistics");
|
|
408
419
|
}
|
package/src/presentation.ts
CHANGED
|
@@ -28,11 +28,11 @@ export function presentJscpdScan(
|
|
|
28
28
|
.slice(0, Math.max(maxFindings, overlayLimit ?? 0))
|
|
29
29
|
.map(presentFinding);
|
|
30
30
|
const findings = Object.freeze(retained.slice(0, maxFindings));
|
|
31
|
-
const omittedFindings = Math.max(0,
|
|
31
|
+
const omittedFindings = Math.max(0, summary.clones - findings.length);
|
|
32
32
|
const overlayCache = overlayLimit
|
|
33
33
|
? Object.freeze({
|
|
34
34
|
findings: Object.freeze(retained.slice(0, overlayLimit)),
|
|
35
|
-
omittedFindings: Math.max(0,
|
|
35
|
+
omittedFindings: Math.max(0, summary.clones - overlayLimit),
|
|
36
36
|
})
|
|
37
37
|
: undefined;
|
|
38
38
|
const outcome = findings.length === 0 ? "clean" : "findings";
|